summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRomain Guy <romainguy@google.com>2011-02-02 20:28:09 -0800
committerRomain Guy <romainguy@google.com>2011-02-02 20:28:09 -0800
commit09b7c91de73b59aa3f679b3ae3ba299f82ec9f8a (patch)
tree9eb49f2fedb60e6df37b54216ed20f054a6f55fd
parent62687ec12cb8e0b1d4044a235b1387b9a8c3b4b4 (diff)
downloadframeworks_base-09b7c91de73b59aa3f679b3ae3ba299f82ec9f8a.zip
frameworks_base-09b7c91de73b59aa3f679b3ae3ba299f82ec9f8a.tar.gz
frameworks_base-09b7c91de73b59aa3f679b3ae3ba299f82ec9f8a.tar.bz2
Allocate layers from the layers pool.
Bug #3413433 This change will be beneficial to Launcher to avoid hiccups when swiping pages of icons. When a layer is discarded, it is kept in the layers pool instead of being destroyed right away. This favors memory reuse over allocations. Change-Id: Ifb6944ba83d6ceb67c331527c0827b26ce648eb1
-rw-r--r--core/java/android/view/GLES20Layer.java7
-rw-r--r--libs/hwui/Caches.cpp8
-rw-r--r--libs/hwui/LayerCache.cpp25
-rw-r--r--libs/hwui/LayerCache.h11
-rw-r--r--libs/hwui/LayerRenderer.cpp128
-rw-r--r--libs/hwui/Properties.h6
-rw-r--r--tests/HwAccelerationTest/AndroidManifest.xml11
-rw-r--r--tests/HwAccelerationTest/res/layout/view_layers_6.xml60
-rw-r--r--tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity6.java131
9 files changed, 306 insertions, 81 deletions
diff --git a/core/java/android/view/GLES20Layer.java b/core/java/android/view/GLES20Layer.java
index 0230430..6000a4a 100644
--- a/core/java/android/view/GLES20Layer.java
+++ b/core/java/android/view/GLES20Layer.java
@@ -66,10 +66,11 @@ class GLES20Layer extends HardwareLayer {
@Override
void resize(int width, int height) {
if (!isValid() || width <= 0 || height <= 0) return;
- if (width > mLayerWidth || height > mLayerHeight) {
- mWidth = width;
- mHeight = height;
+ mWidth = width;
+ mHeight = height;
+
+ if (width != mLayerWidth || height != mLayerHeight) {
int[] layerInfo = new int[2];
GLES20Canvas.nResizeLayer(mLayer, width, height, layerInfo);
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index bffab95..ebf7aa0 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -20,6 +20,7 @@
#include "Caches.h"
#include "Properties.h"
+#include "LayerRenderer.h"
namespace android {
@@ -116,12 +117,7 @@ void Caches::clearGarbage() {
size_t count = mLayerGarbage.size();
for (size_t i = 0; i < count; i++) {
Layer* layer = mLayerGarbage.itemAt(i);
- if (layer) {
- if (layer->fbo) glDeleteFramebuffers(1, &layer->fbo);
- if (layer->texture) glDeleteTextures(1, &layer->texture);
-
- delete layer;
- }
+ LayerRenderer::destroyLayer(layer);
}
mLayerGarbage.clear();
}
diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp
index 7667af5..a9710ad 100644
--- a/libs/hwui/LayerCache.cpp
+++ b/libs/hwui/LayerCache.cpp
@@ -128,6 +128,31 @@ Layer* LayerCache::get(const uint32_t width, const uint32_t height) {
return layer;
}
+bool LayerCache::resize(Layer* layer, const uint32_t width, const uint32_t height) {
+ // TODO: We should be smarter and see if we have a texture of the appropriate
+ // size already in the cache, and reuse it instead of creating a new one
+
+ LayerEntry entry(width, height);
+ if (entry.mWidth <= layer->width && entry.mHeight <= layer->height) {
+ return true;
+ }
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, layer->texture);
+
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, entry.mWidth, entry.mHeight, 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+
+ if (glGetError() != GL_NO_ERROR) {
+ return false;
+ }
+
+ layer->width = entry.mWidth;
+ layer->height = entry.mHeight;
+
+ return true;
+}
+
bool LayerCache::put(Layer* layer) {
const uint32_t size = layer->width * layer->height * 4;
// Don't even try to cache a layer that's bigger than the cache
diff --git a/libs/hwui/LayerCache.h b/libs/hwui/LayerCache.h
index 1333a73..d2d5f39 100644
--- a/libs/hwui/LayerCache.h
+++ b/libs/hwui/LayerCache.h
@@ -76,6 +76,17 @@ public:
* Clears the cache. This causes all layers to be deleted.
*/
void clear();
+ /**
+ * Resize the specified layer if needed.
+ *
+ * @param layer The layer to resize
+ * @param width The new width of the layer
+ * @param height The new height of the layer
+ *
+ * @return True if the layer was resized or nothing happened, false if
+ * a failure occurred during the resizing operation
+ */
+ bool resize(Layer* layer, const uint32_t width, const uint32_t height);
/**
* Sets the maximum size of the cache in bytes.
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index 60ff0bf..24f9739 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -18,6 +18,7 @@
#include <ui/Rect.h>
+#include "LayerCache.h"
#include "LayerRenderer.h"
#include "Properties.h"
#include "Rect.h"
@@ -34,21 +35,24 @@ void LayerRenderer::prepareDirty(float left, float top, float right, float botto
glBindFramebuffer(GL_FRAMEBUFFER, mLayer->fbo);
+ const float width = mLayer->layer.getWidth();
+ const float height = mLayer->layer.getHeight();
+
#if RENDER_LAYERS_AS_REGIONS
Rect dirty(left, top, right, bottom);
if (dirty.isEmpty() || (dirty.left <= 0 && dirty.top <= 0 &&
- dirty.right >= mLayer->width && dirty.bottom >= mLayer->height)) {
+ dirty.right >= width && dirty.bottom >= height)) {
mLayer->region.clear();
- dirty.set(0.0f, 0.0f, mLayer->width, mLayer->height);
+ dirty.set(0.0f, 0.0f, width, height);
} else {
- dirty.intersect(0.0f, 0.0f, mLayer->width, mLayer->height);
+ dirty.intersect(0.0f, 0.0f, width, height);
android::Rect r(dirty.left, dirty.top, dirty.right, dirty.bottom);
mLayer->region.subtractSelf(r);
}
OpenGLRenderer::prepareDirty(dirty.left, dirty.top, dirty.right, dirty.bottom, opaque);
#else
- OpenGLRenderer::prepareDirty(0.0f, 0.0f, mLayer->width, mLayer->height, opaque);
+ OpenGLRenderer::prepareDirty(0.0f, 0.0f, width, height, opaque);
#endif
}
@@ -162,64 +166,56 @@ void LayerRenderer::generateMesh() {
Layer* LayerRenderer::createLayer(uint32_t width, uint32_t height, bool isOpaque) {
LAYER_RENDERER_LOGD("Creating new layer %dx%d", width, height);
- Layer* layer = new Layer(width, height);
-
- GLuint previousFbo;
- glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo);
-
- glGenFramebuffers(1, &layer->fbo);
- glBindFramebuffer(GL_FRAMEBUFFER, layer->fbo);
-
- if (glGetError() != GL_NO_ERROR) {
- glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
- glDeleteBuffers(1, &layer->fbo);
- return 0;
+ GLuint fbo = Caches::getInstance().fboCache.get();
+ if (!fbo) {
+ LOGW("Could not obtain an FBO");
+ return NULL;
}
glActiveTexture(GL_TEXTURE0);
- glGenTextures(1, &layer->texture);
- glBindTexture(GL_TEXTURE_2D, layer->texture);
+ Layer* layer = Caches::getInstance().layerCache.get(width, height);
+ if (!layer) {
+ LOGW("Could not obtain a layer");
+ return NULL;
+ }
- glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
+ layer->fbo = fbo;
+ layer->layer.set(0.0f, 0.0f, width, height);
+ layer->texCoords.set(0.0f, height / float(layer->height),
+ width / float(layer->width), 0.0f);
+ layer->alpha = 255;
+ layer->mode = SkXfermode::kSrcOver_Mode;
+ layer->blend = !isOpaque;
+ layer->colorFilter = NULL;
+ layer->region.clear();
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ GLuint previousFbo;
+ glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glBindFramebuffer(GL_FRAMEBUFFER, layer->fbo);
+ glBindTexture(GL_TEXTURE_2D, layer->texture);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
- GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+ // Initialize the texture if needed
+ if (layer->empty) {
+ layer->empty = false;
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, layer->width, layer->height, 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, NULL);
- if (glGetError() != GL_NO_ERROR) {
- glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
- glDeleteBuffers(1, &layer->fbo);
- glDeleteTextures(1, &layer->texture);
- delete layer;
- return 0;
+ if (glGetError() != GL_NO_ERROR) {
+ LOGD("Could not allocate texture");
+ glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
+ glDeleteTextures(1, &layer->texture);
+ Caches::getInstance().fboCache.put(fbo);
+ delete layer;
+ return NULL;
+ }
}
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
layer->texture, 0);
- if (glGetError() != GL_NO_ERROR) {
- glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
- glDeleteBuffers(1, &layer->fbo);
- glDeleteTextures(1, &layer->texture);
- delete layer;
- return 0;
- }
-
glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
- layer->layer.set(0.0f, 0.0f, width, height);
- layer->texCoords.set(0.0f, 1.0f, 1.0f, 0.0f);
- layer->alpha = 255;
- layer->mode = SkXfermode::kSrcOver_Mode;
- layer->blend = !isOpaque;
- layer->empty = false;
- layer->colorFilter = NULL;
-
return layer;
}
@@ -227,27 +223,17 @@ bool LayerRenderer::resizeLayer(Layer* layer, uint32_t width, uint32_t height) {
if (layer) {
LAYER_RENDERER_LOGD("Resizing layer fbo = %d to %dx%d", layer->fbo, width, height);
- glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, layer->texture);
-
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
- GL_RGBA, GL_UNSIGNED_BYTE, NULL);
-
- if (glGetError() != GL_NO_ERROR) {
- glDeleteBuffers(1, &layer->fbo);
- glDeleteTextures(1, &layer->texture);
-
- layer->width = 0;
- layer->height = 0;
- layer->fbo = 0;
- layer->texture = 0;
-
+ if (Caches::getInstance().layerCache.resize(layer, width, height)) {
+ layer->layer.set(0.0f, 0.0f, width, height);
+ layer->texCoords.set(0.0f, height / float(layer->height),
+ width / float(layer->width), 0.0f);
+ } else {
+ if (layer->texture) glDeleteTextures(1, &layer->texture);
+ delete layer;
return false;
}
-
- layer->width = width;
- layer->height = height;
}
+
return true;
}
@@ -255,10 +241,16 @@ void LayerRenderer::destroyLayer(Layer* layer) {
if (layer) {
LAYER_RENDERER_LOGD("Destroying layer, fbo = %d", layer->fbo);
- if (layer->fbo) glDeleteFramebuffers(1, &layer->fbo);
- if (layer->texture) glDeleteTextures(1, &layer->texture);
+ if (layer->fbo) {
+ Caches::getInstance().fboCache.put(layer->fbo);
+ }
- delete layer;
+ if (!Caches::getInstance().layerCache.put(layer)) {
+ if (layer->texture) glDeleteTextures(1, &layer->texture);
+ delete layer;
+ } else {
+ layer->region.clear();
+ }
}
}
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 2f230b5..2d8b6f3 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -64,14 +64,14 @@ enum DebugLevel {
// Converts a number of mega-bytes into bytes
#define MB(s) s * 1024 * 1024
-#define DEFAULT_TEXTURE_CACHE_SIZE 20.0f
-#define DEFAULT_LAYER_CACHE_SIZE 8.0f
+#define DEFAULT_TEXTURE_CACHE_SIZE 24.0f
+#define DEFAULT_LAYER_CACHE_SIZE 24.0f
#define DEFAULT_PATH_CACHE_SIZE 4.0f
#define DEFAULT_SHAPE_CACHE_SIZE 1.0f
#define DEFAULT_PATCH_CACHE_SIZE 512
#define DEFAULT_GRADIENT_CACHE_SIZE 0.5f
#define DEFAULT_DROP_SHADOW_CACHE_SIZE 2.0f
-#define DEFAULT_FBO_CACHE_SIZE 12
+#define DEFAULT_FBO_CACHE_SIZE 16
#define DEFAULT_TEXT_GAMMA 1.4f
#define DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD 64
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index fc50334..3535809 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -96,7 +96,16 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
-
+
+ <activity
+ android:name="ViewLayersActivity6"
+ android:label="_ViewLayers6">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
<activity
android:name="AlphaLayersActivity"
android:label="_αLayers">
diff --git a/tests/HwAccelerationTest/res/layout/view_layers_6.xml b/tests/HwAccelerationTest/res/layout/view_layers_6.xml
new file mode 100644
index 0000000..36cf8c9
--- /dev/null
+++ b/tests/HwAccelerationTest/res/layout/view_layers_6.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="0dip"
+ android:layout_height="match_parent"
+ android:layout_weight="1">
+
+ <Button
+ android:onClick="enableLayer"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Enable layer" />
+
+ <Button
+ android:onClick="disableLayer"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Disable layer" />
+
+ <Button
+ android:onClick="shrinkLayer"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Shrink layer" />
+
+ <Button
+ android:onClick="growLayer"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Grow layer" />
+
+ </LinearLayout>
+
+ <ListView
+ android:id="@+id/list1"
+ android:layout_width="0dip"
+ android:layout_height="match_parent"
+ android:layout_weight="1" />
+
+</LinearLayout>
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity6.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity6.java
new file mode 100644
index 0000000..2edfec7
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity6.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.os.Bundle;
+import android.util.DisplayMetrics;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class ViewLayersActivity6 extends Activity {
+ private final Paint mPaint = new Paint();
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.view_layers_6);
+
+ mPaint.setColorFilter(new PorterDuffColorFilter(0xff00ff00, PorterDuff.Mode.MULTIPLY));
+
+ setupList(R.id.list1);
+ }
+
+ public void enableLayer(View v) {
+ findViewById(R.id.list1).setLayerType(View.LAYER_TYPE_HARDWARE, mPaint);
+ }
+
+ public void disableLayer(View v) {
+ findViewById(R.id.list1).setLayerType(View.LAYER_TYPE_NONE, null);
+ }
+
+ public void growLayer(View v) {
+ findViewById(R.id.list1).getLayoutParams().height = ViewGroup.LayoutParams.MATCH_PARENT;
+ findViewById(R.id.list1).requestLayout();
+ }
+
+ public void shrinkLayer(View v) {
+ findViewById(R.id.list1).getLayoutParams().height = 300;
+ findViewById(R.id.list1).requestLayout();
+ }
+
+ private void setupList(int listId) {
+ final ListView list = (ListView) findViewById(listId);
+ list.setAdapter(new SimpleListAdapter(this));
+ }
+
+ private static class SimpleListAdapter extends ArrayAdapter<String> {
+ public SimpleListAdapter(Context context) {
+ super(context, android.R.layout.simple_list_item_1, DATA_LIST);
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ TextView v = (TextView) super.getView(position, convertView, parent);
+ final Resources r = getContext().getResources();
+ final DisplayMetrics metrics = r.getDisplayMetrics();
+ v.setCompoundDrawablePadding((int) (6 * metrics.density + 0.5f));
+ v.setCompoundDrawablesWithIntrinsicBounds(r.getDrawable(R.drawable.icon),
+ null, null, null);
+ return v;
+ }
+ }
+
+ private static final String[] DATA_LIST = {
+ "Afghanistan", "Albania", "Algeria", "American Samoa", "Andorra",
+ "Angola", "Anguilla", "Antarctica", "Antigua and Barbuda", "Argentina",
+ "Armenia", "Aruba", "Australia", "Austria", "Azerbaijan",
+ "Bahrain", "Bangladesh", "Barbados", "Belarus", "Belgium",
+ "Belize", "Benin", "Bermuda", "Bhutan", "Bolivia",
+ "Bosnia and Herzegovina", "Botswana", "Bouvet Island", "Brazil",
+ "British Indian Ocean Territory", "British Virgin Islands", "Brunei", "Bulgaria",
+ "Burkina Faso", "Burundi", "Cote d'Ivoire", "Cambodia", "Cameroon", "Canada", "Cape Verde",
+ "Cayman Islands", "Central African Republic", "Chad", "Chile", "China",
+ "Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo",
+ "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czech Republic",
+ "Democratic Republic of the Congo", "Denmark", "Djibouti", "Dominica", "Dominican Republic",
+ "East Timor", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea",
+ "Estonia", "Ethiopia", "Faeroe Islands", "Falkland Islands", "Fiji", "Finland",
+ "Former Yugoslav Republic of Macedonia", "France", "French Guiana", "French Polynesia",
+ "French Southern Territories", "Gabon", "Georgia", "Germany", "Ghana", "Gibraltar",
+ "Greece", "Greenland", "Grenada", "Guadeloupe", "Guam", "Guatemala", "Guinea", "Guinea-Bissau",
+ "Guyana", "Haiti", "Heard Island and McDonald Islands", "Honduras", "Hong Kong", "Hungary",
+ "Iceland", "India", "Indonesia", "Iran", "Iraq", "Ireland", "Israel", "Italy", "Jamaica",
+ "Japan", "Jordan", "Kazakhstan", "Kenya", "Kiribati", "Kuwait", "Kyrgyzstan", "Laos",
+ "Latvia", "Lebanon", "Lesotho", "Liberia", "Libya", "Liechtenstein", "Lithuania", "Luxembourg",
+ "Macau", "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali", "Malta", "Marshall Islands",
+ "Martinique", "Mauritania", "Mauritius", "Mayotte", "Mexico", "Micronesia", "Moldova",
+ "Monaco", "Mongolia", "Montserrat", "Morocco", "Mozambique", "Myanmar", "Namibia",
+ "Nauru", "Nepal", "Netherlands", "Netherlands Antilles", "New Caledonia", "New Zealand",
+ "Nicaragua", "Niger", "Nigeria", "Niue", "Norfolk Island", "North Korea", "Northern Marianas",
+ "Norway", "Oman", "Pakistan", "Palau", "Panama", "Papua New Guinea", "Paraguay", "Peru",
+ "Philippines", "Pitcairn Islands", "Poland", "Portugal", "Puerto Rico", "Qatar",
+ "Reunion", "Romania", "Russia", "Rwanda", "Sqo Tome and Principe", "Saint Helena",
+ "Saint Kitts and Nevis", "Saint Lucia", "Saint Pierre and Miquelon",
+ "Saint Vincent and the Grenadines", "Samoa", "San Marino", "Saudi Arabia", "Senegal",
+ "Seychelles", "Sierra Leone", "Singapore", "Slovakia", "Slovenia", "Solomon Islands",
+ "Somalia", "South Africa", "South Georgia and the South Sandwich Islands", "South Korea",
+ "Spain", "Sri Lanka", "Sudan", "Suriname", "Svalbard and Jan Mayen", "Swaziland", "Sweden",
+ "Switzerland", "Syria", "Taiwan", "Tajikistan", "Tanzania", "Thailand", "The Bahamas",
+ "The Gambia", "Togo", "Tokelau", "Tonga", "Trinidad and Tobago", "Tunisia", "Turkey",
+ "Turkmenistan", "Turks and Caicos Islands", "Tuvalu", "Virgin Islands", "Uganda",
+ "Ukraine", "United Arab Emirates", "United Kingdom",
+ "United States", "United States Minor Outlying Islands", "Uruguay", "Uzbekistan",
+ "Vanuatu", "Vatican City", "Venezuela", "Vietnam", "Wallis and Futuna", "Western Sahara",
+ "Yemen", "Yugoslavia", "Zambia", "Zimbabwe"
+ };
+}