| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "ui/android/resources/resource_manager_impl.h" | 5 #include "ui/android/resources/resource_manager_impl.h" |
| 6 | 6 |
| 7 #include <stddef.h> | 7 #include <stddef.h> |
| 8 | 8 |
| 9 #include <utility> | 9 #include <utility> |
| 10 #include <vector> | 10 #include <vector> |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 60 ResourceManagerImpl::GetJavaObject() { | 60 ResourceManagerImpl::GetJavaObject() { |
| 61 return base::android::ScopedJavaLocalRef<jobject>(java_obj_); | 61 return base::android::ScopedJavaLocalRef<jobject>(java_obj_); |
| 62 } | 62 } |
| 63 | 63 |
| 64 ResourceManager::Resource* ResourceManagerImpl::GetResource( | 64 ResourceManager::Resource* ResourceManagerImpl::GetResource( |
| 65 AndroidResourceType res_type, | 65 AndroidResourceType res_type, |
| 66 int res_id) { | 66 int res_id) { |
| 67 DCHECK_GE(res_type, ANDROID_RESOURCE_TYPE_FIRST); | 67 DCHECK_GE(res_type, ANDROID_RESOURCE_TYPE_FIRST); |
| 68 DCHECK_LE(res_type, ANDROID_RESOURCE_TYPE_LAST); | 68 DCHECK_LE(res_type, ANDROID_RESOURCE_TYPE_LAST); |
| 69 | 69 |
| 70 Resource* resource = resources_[res_type].Lookup(res_id); | 70 std::unordered_map<int, std::unique_ptr<Resource>>::iterator item = |
| 71 resources_[res_type].find(res_id); |
| 71 | 72 |
| 72 if (!resource || res_type == ANDROID_RESOURCE_TYPE_DYNAMIC || | 73 if (item == resources_[res_type].end() || |
| 74 res_type == ANDROID_RESOURCE_TYPE_DYNAMIC || |
| 73 res_type == ANDROID_RESOURCE_TYPE_DYNAMIC_BITMAP) { | 75 res_type == ANDROID_RESOURCE_TYPE_DYNAMIC_BITMAP) { |
| 74 RequestResourceFromJava(res_type, res_id); | 76 RequestResourceFromJava(res_type, res_id); |
| 75 resource = resources_[res_type].Lookup(res_id); | 77 |
| 78 // Check if the resource has been added (some dynamic may not have been). |
| 79 item = resources_[res_type].find(res_id); |
| 80 if (item == resources_[res_type].end()) |
| 81 return nullptr; |
| 76 } | 82 } |
| 77 | 83 |
| 78 return resource; | 84 return item->second.get(); |
| 79 } | 85 } |
| 80 | 86 |
| 81 void ResourceManagerImpl::RemoveUnusedTints( | 87 void ResourceManagerImpl::RemoveUnusedTints( |
| 82 const std::unordered_set<int>& used_tints) { | 88 const std::unordered_set<int>& used_tints) { |
| 83 // Iterate over the currently cached tints and remove ones that were not | 89 // Iterate over the currently cached tints and remove ones that were not |
| 84 // used as defined in |used_tints|. | 90 // used as defined in |used_tints|. |
| 85 for (auto it = tinted_resources_.cbegin(); it != tinted_resources_.cend();) { | 91 for (auto it = tinted_resources_.cbegin(); it != tinted_resources_.cend();) { |
| 86 if (used_tints.find(it->first) == used_tints.end()) { | 92 if (used_tints.find(it->first) == used_tints.end()) { |
| 87 it = tinted_resources_.erase(it); | 93 it = tinted_resources_.erase(it); |
| 88 } else { | 94 } else { |
| 89 ++it; | 95 ++it; |
| 90 } | 96 } |
| 91 } | 97 } |
| 92 } | 98 } |
| 93 | 99 |
| 94 ResourceManager::Resource* ResourceManagerImpl::GetStaticResourceWithTint( | 100 ResourceManager::Resource* ResourceManagerImpl::GetStaticResourceWithTint( |
| 95 int res_id, | 101 int res_id, |
| 96 int tint_color) { | 102 SkColor tint_color) { |
| 97 if (tinted_resources_.find(tint_color) == tinted_resources_.end()) { | 103 if (tinted_resources_.find(tint_color) == tinted_resources_.end()) { |
| 98 tinted_resources_[tint_color] = base::MakeUnique<ResourceMap>(); | 104 tinted_resources_[tint_color] = base::MakeUnique<ResourceMap>(); |
| 99 } | 105 } |
| 100 ResourceMap* resource_map = tinted_resources_[tint_color].get(); | 106 ResourceMap* resource_map = tinted_resources_[tint_color].get(); |
| 101 | 107 |
| 102 Resource* tinted_resource = resource_map->Lookup(res_id); | 108 // If the resource is already cached, use it. |
| 109 std::unordered_map<int, std::unique_ptr<Resource>>::iterator item = |
| 110 resource_map->find(res_id); |
| 111 if (item != resource_map->end()) |
| 112 return item->second.get(); |
| 103 | 113 |
| 104 // If the resource is already cached, use it. | 114 std::unique_ptr<Resource> tinted_resource = base::MakeUnique<Resource>(); |
| 105 if (tinted_resource) | |
| 106 return tinted_resource; | |
| 107 | |
| 108 tinted_resource = new Resource(); | |
| 109 | 115 |
| 110 ResourceManager::Resource* base_image = | 116 ResourceManager::Resource* base_image = |
| 111 GetResource(ANDROID_RESOURCE_TYPE_STATIC, res_id); | 117 GetResource(ANDROID_RESOURCE_TYPE_STATIC, res_id); |
| 112 DCHECK(base_image); | 118 DCHECK(base_image); |
| 113 | 119 |
| 114 TRACE_EVENT0("browser", "ResourceManagerImpl::GetStaticResourceWithTint"); | 120 TRACE_EVENT0("browser", "ResourceManagerImpl::GetStaticResourceWithTint"); |
| 115 SkBitmap tinted_bitmap; | 121 SkBitmap tinted_bitmap; |
| 116 tinted_bitmap.allocPixels(SkImageInfo::MakeN32Premul(base_image->size.width(), | 122 tinted_bitmap.allocPixels(SkImageInfo::MakeN32Premul(base_image->size.width(), |
| 117 base_image->size.height())); | 123 base_image->size.height())); |
| 118 | 124 |
| 119 SkCanvas canvas(tinted_bitmap); | 125 SkCanvas canvas(tinted_bitmap); |
| 120 canvas.clear(SK_ColorTRANSPARENT); | 126 canvas.clear(SK_ColorTRANSPARENT); |
| 121 | 127 |
| 122 // Build a color filter to use on the base resource. This filter multiplies | 128 // Build a color filter to use on the base resource. This filter multiplies |
| 123 // the RGB components by the components of the new color but retains the | 129 // the RGB components by the components of the new color but retains the |
| 124 // alpha of the original image. | 130 // alpha of the original image. |
| 125 SkPaint color_filter; | 131 SkPaint color_filter; |
| 126 color_filter.setColorFilter( | 132 color_filter.setColorFilter( |
| 127 SkColorFilter::MakeModeFilter(tint_color, SkXfermode::kModulate_Mode)); | 133 SkColorFilter::MakeModeFilter(tint_color, SkXfermode::kModulate_Mode)); |
| 128 | 134 |
| 129 // Draw the resource and make it immutable. | 135 // Draw the resource and make it immutable. |
| 130 base_image->ui_resource->GetBitmap(base_image->ui_resource->id(), false) | 136 base_image->ui_resource->GetBitmap(base_image->ui_resource->id(), false) |
| 131 .DrawToCanvas(&canvas, &color_filter); | 137 .DrawToCanvas(&canvas, &color_filter); |
| 132 tinted_bitmap.setImmutable(); | 138 tinted_bitmap.setImmutable(); |
| 133 | 139 |
| 134 // Create a UI resource from the new bitmap. | 140 // Create a UI resource from the new bitmap. |
| 135 tinted_resource = new Resource(); | |
| 136 tinted_resource->size = gfx::Size(base_image->size); | 141 tinted_resource->size = gfx::Size(base_image->size); |
| 137 tinted_resource->padding = gfx::Rect(base_image->padding); | 142 tinted_resource->padding = gfx::Rect(base_image->padding); |
| 138 tinted_resource->aperture = gfx::Rect(base_image->aperture); | 143 tinted_resource->aperture = gfx::Rect(base_image->aperture); |
| 139 tinted_resource->ui_resource = cc::ScopedUIResource::Create( | 144 tinted_resource->ui_resource = cc::ScopedUIResource::Create( |
| 140 ui_resource_manager_, cc::UIResourceBitmap(tinted_bitmap)); | 145 ui_resource_manager_, cc::UIResourceBitmap(tinted_bitmap)); |
| 141 | 146 |
| 142 resource_map->AddWithID(tinted_resource, res_id); | 147 (*resource_map)[res_id].swap(tinted_resource); |
| 143 | 148 |
| 144 return tinted_resource; | 149 return (*resource_map)[res_id].get(); |
| 145 } | 150 } |
| 146 | 151 |
| 147 void ResourceManagerImpl::ClearTintedResourceCache(JNIEnv* env, | 152 void ResourceManagerImpl::ClearTintedResourceCache(JNIEnv* env, |
| 148 const JavaRef<jobject>& jobj) { | 153 const JavaRef<jobject>& jobj) { |
| 149 tinted_resources_.clear(); | 154 tinted_resources_.clear(); |
| 150 } | 155 } |
| 151 | 156 |
| 152 void ResourceManagerImpl::PreloadResource(AndroidResourceType res_type, | 157 void ResourceManagerImpl::PreloadResource(AndroidResourceType res_type, |
| 153 int res_id) { | 158 int res_id) { |
| 154 DCHECK_GE(res_type, ANDROID_RESOURCE_TYPE_FIRST); | 159 DCHECK_GE(res_type, ANDROID_RESOURCE_TYPE_FIRST); |
| 155 DCHECK_LE(res_type, ANDROID_RESOURCE_TYPE_LAST); | 160 DCHECK_LE(res_type, ANDROID_RESOURCE_TYPE_LAST); |
| 156 | 161 |
| 157 // Don't send out a query if the resource is already loaded. | 162 // Don't send out a query if the resource is already loaded. |
| 158 if (resources_[res_type].Lookup(res_id)) | 163 if (resources_[res_type].find(res_id) != resources_[res_type].end()) |
| 159 return; | 164 return; |
| 160 | 165 |
| 161 PreloadResourceFromJava(res_type, res_id); | 166 PreloadResourceFromJava(res_type, res_id); |
| 162 } | 167 } |
| 163 | 168 |
| 164 void ResourceManagerImpl::OnResourceReady(JNIEnv* env, | 169 void ResourceManagerImpl::OnResourceReady(JNIEnv* env, |
| 165 const JavaRef<jobject>& jobj, | 170 const JavaRef<jobject>& jobj, |
| 166 jint res_type, | 171 jint res_type, |
| 167 jint res_id, | 172 jint res_id, |
| 168 const JavaRef<jobject>& bitmap, | 173 const JavaRef<jobject>& bitmap, |
| 169 jint padding_left, | 174 jint padding_left, |
| 170 jint padding_top, | 175 jint padding_top, |
| 171 jint padding_right, | 176 jint padding_right, |
| 172 jint padding_bottom, | 177 jint padding_bottom, |
| 173 jint aperture_left, | 178 jint aperture_left, |
| 174 jint aperture_top, | 179 jint aperture_top, |
| 175 jint aperture_right, | 180 jint aperture_right, |
| 176 jint aperture_bottom) { | 181 jint aperture_bottom) { |
| 177 DCHECK_GE(res_type, ANDROID_RESOURCE_TYPE_FIRST); | 182 DCHECK_GE(res_type, ANDROID_RESOURCE_TYPE_FIRST); |
| 178 DCHECK_LE(res_type, ANDROID_RESOURCE_TYPE_LAST); | 183 DCHECK_LE(res_type, ANDROID_RESOURCE_TYPE_LAST); |
| 179 TRACE_EVENT2("ui", "ResourceManagerImpl::OnResourceReady", | 184 TRACE_EVENT2("ui", "ResourceManagerImpl::OnResourceReady", |
| 180 "resource_type", res_type, | 185 "resource_type", res_type, |
| 181 "resource_id", res_id); | 186 "resource_id", res_id); |
| 182 | 187 |
| 183 Resource* resource = resources_[res_type].Lookup(res_id); | 188 std::unordered_map<int, std::unique_ptr<Resource>>::iterator item = |
| 184 if (!resource) { | 189 resources_[res_type].find(res_id); |
| 185 resource = new Resource(); | 190 if (item == resources_[res_type].end()) { |
| 186 resources_[res_type].AddWithID(resource, res_id); | 191 resources_[res_type][res_id] = base::MakeUnique<Resource>(); |
| 187 } | 192 } |
| 188 | 193 |
| 194 Resource* resource = resources_[res_type][res_id].get(); |
| 195 |
| 189 gfx::JavaBitmap jbitmap(bitmap.obj()); | 196 gfx::JavaBitmap jbitmap(bitmap.obj()); |
| 190 resource->size = jbitmap.size(); | 197 resource->size = jbitmap.size(); |
| 191 resource->padding.SetRect(padding_left, padding_top, | 198 resource->padding.SetRect(padding_left, padding_top, |
| 192 padding_right - padding_left, | 199 padding_right - padding_left, |
| 193 padding_bottom - padding_top); | 200 padding_bottom - padding_top); |
| 194 resource->aperture.SetRect(aperture_left, aperture_top, | 201 resource->aperture.SetRect(aperture_left, aperture_top, |
| 195 aperture_right - aperture_left, | 202 aperture_right - aperture_left, |
| 196 aperture_bottom - aperture_top); | 203 aperture_bottom - aperture_top); |
| 197 | 204 |
| 198 SkBitmap skbitmap = gfx::CreateSkBitmapFromJavaBitmap(jbitmap); | 205 SkBitmap skbitmap = gfx::CreateSkBitmapFromJavaBitmap(jbitmap); |
| 199 skbitmap.setImmutable(); | 206 skbitmap.setImmutable(); |
| 200 resource->ui_resource = cc::ScopedUIResource::Create( | 207 resource->ui_resource = cc::ScopedUIResource::Create( |
| 201 ui_resource_manager_, cc::UIResourceBitmap(skbitmap)); | 208 ui_resource_manager_, cc::UIResourceBitmap(skbitmap)); |
| 202 } | 209 } |
| 203 | 210 |
| 204 CrushedSpriteResource* ResourceManagerImpl::GetCrushedSpriteResource( | 211 CrushedSpriteResource* ResourceManagerImpl::GetCrushedSpriteResource( |
| 205 int bitmap_res_id, int metadata_res_id) { | 212 int bitmap_res_id, int metadata_res_id) { |
| 206 CrushedSpriteResource* resource = | 213 |
| 207 crushed_sprite_resources_.Lookup(bitmap_res_id); | 214 CrushedSpriteResource* resource = nullptr; |
| 215 if (crushed_sprite_resources_.find(bitmap_res_id) |
| 216 != crushed_sprite_resources_.end()) { |
| 217 resource = crushed_sprite_resources_[bitmap_res_id].get(); |
| 218 } |
| 219 |
| 208 if (!resource) { | 220 if (!resource) { |
| 209 RequestCrushedSpriteResourceFromJava(bitmap_res_id, metadata_res_id, false); | 221 RequestCrushedSpriteResourceFromJava(bitmap_res_id, metadata_res_id, false); |
| 210 resource = crushed_sprite_resources_.Lookup(bitmap_res_id); | 222 resource = crushed_sprite_resources_[bitmap_res_id].get(); |
| 211 } else if (resource->BitmapHasBeenEvictedFromMemory()) { | 223 } else if (resource->BitmapHasBeenEvictedFromMemory()) { |
| 212 RequestCrushedSpriteResourceFromJava(bitmap_res_id, metadata_res_id, true); | 224 RequestCrushedSpriteResourceFromJava(bitmap_res_id, metadata_res_id, true); |
| 213 } | 225 } |
| 214 | 226 |
| 215 return resource; | 227 return resource; |
| 216 } | 228 } |
| 217 | 229 |
| 218 void ResourceManagerImpl::OnCrushedSpriteResourceReady( | 230 void ResourceManagerImpl::OnCrushedSpriteResourceReady( |
| 219 JNIEnv* env, | 231 JNIEnv* env, |
| 220 const JavaRef<jobject>& jobj, | 232 const JavaRef<jobject>& jobj, |
| 221 jint bitmap_res_id, | 233 jint bitmap_res_id, |
| 222 const JavaRef<jobject>& bitmap, | 234 const JavaRef<jobject>& bitmap, |
| 223 const JavaRef<jobjectArray>& frame_rects, | 235 const JavaRef<jobjectArray>& frame_rects, |
| 224 jint unscaled_sprite_width, | 236 jint unscaled_sprite_width, |
| 225 jint unscaled_sprite_height, | 237 jint unscaled_sprite_height, |
| 226 jfloat scaled_sprite_width, | 238 jfloat scaled_sprite_width, |
| 227 jfloat scaled_sprite_height) { | 239 jfloat scaled_sprite_height) { |
| 228 // Construct source and destination rectangles for each frame from | 240 // Construct source and destination rectangles for each frame from |
| 229 // |frame_rects|. | 241 // |frame_rects|. |
| 230 std::vector<std::vector<int>> all_frame_rects_vector; | 242 std::vector<std::vector<int>> all_frame_rects_vector; |
| 231 JavaArrayOfIntArrayToIntVector(env, frame_rects.obj(), | 243 JavaArrayOfIntArrayToIntVector(env, frame_rects.obj(), |
| 232 &all_frame_rects_vector); | 244 &all_frame_rects_vector); |
| 233 CrushedSpriteResource::SrcDstRects src_dst_rects = | 245 CrushedSpriteResource::SrcDstRects src_dst_rects = |
| 234 ProcessCrushedSpriteFrameRects(all_frame_rects_vector); | 246 ProcessCrushedSpriteFrameRects(all_frame_rects_vector); |
| 235 | 247 |
| 236 SkBitmap skbitmap = | 248 SkBitmap skbitmap = |
| 237 gfx::CreateSkBitmapFromJavaBitmap(gfx::JavaBitmap(bitmap.obj())); | 249 gfx::CreateSkBitmapFromJavaBitmap(gfx::JavaBitmap(bitmap.obj())); |
| 238 | 250 |
| 239 CrushedSpriteResource* resource = new CrushedSpriteResource( | 251 std::unique_ptr<CrushedSpriteResource> resource = |
| 240 skbitmap, | 252 base::MakeUnique<CrushedSpriteResource>( |
| 241 src_dst_rects, | 253 skbitmap, |
| 242 gfx::Size(unscaled_sprite_width, unscaled_sprite_height), | 254 src_dst_rects, |
| 243 gfx::Size(scaled_sprite_width, scaled_sprite_height)); | 255 gfx::Size(unscaled_sprite_width, unscaled_sprite_height), |
| 256 gfx::Size(scaled_sprite_width, scaled_sprite_height)); |
| 244 | 257 |
| 245 if (crushed_sprite_resources_.Lookup(bitmap_res_id)) { | 258 crushed_sprite_resources_[bitmap_res_id].swap(resource); |
| 246 crushed_sprite_resources_.Replace(bitmap_res_id, resource); | |
| 247 } else { | |
| 248 crushed_sprite_resources_.AddWithID(resource, bitmap_res_id); | |
| 249 } | |
| 250 } | 259 } |
| 251 | 260 |
| 252 CrushedSpriteResource::SrcDstRects | 261 CrushedSpriteResource::SrcDstRects |
| 253 ResourceManagerImpl::ProcessCrushedSpriteFrameRects( | 262 ResourceManagerImpl::ProcessCrushedSpriteFrameRects( |
| 254 std::vector<std::vector<int>> frame_rects_vector) { | 263 std::vector<std::vector<int>> frame_rects_vector) { |
| 255 CrushedSpriteResource::SrcDstRects src_dst_rects; | 264 CrushedSpriteResource::SrcDstRects src_dst_rects; |
| 256 for (size_t i = 0; i < frame_rects_vector.size(); ++i) { | 265 for (size_t i = 0; i < frame_rects_vector.size(); ++i) { |
| 257 std::vector<int> frame_ints = frame_rects_vector[i]; | 266 std::vector<int> frame_ints = frame_rects_vector[i]; |
| 258 CrushedSpriteResource::FrameSrcDstRects frame_src_dst_rects; | 267 CrushedSpriteResource::FrameSrcDstRects frame_src_dst_rects; |
| 259 | 268 |
| (...skipping 16 matching lines...) Expand all Loading... |
| 276 src_dst_rects.push_back(frame_src_dst_rects); | 285 src_dst_rects.push_back(frame_src_dst_rects); |
| 277 } | 286 } |
| 278 return src_dst_rects; | 287 return src_dst_rects; |
| 279 } | 288 } |
| 280 | 289 |
| 281 void ResourceManagerImpl::OnCrushedSpriteResourceReloaded( | 290 void ResourceManagerImpl::OnCrushedSpriteResourceReloaded( |
| 282 JNIEnv* env, | 291 JNIEnv* env, |
| 283 const JavaRef<jobject>& jobj, | 292 const JavaRef<jobject>& jobj, |
| 284 jint bitmap_res_id, | 293 jint bitmap_res_id, |
| 285 const JavaRef<jobject>& bitmap) { | 294 const JavaRef<jobject>& bitmap) { |
| 286 CrushedSpriteResource* resource = | 295 std::unordered_map<int, std::unique_ptr<CrushedSpriteResource>>::iterator |
| 287 crushed_sprite_resources_.Lookup(bitmap_res_id); | 296 item = crushed_sprite_resources_.find(bitmap_res_id); |
| 288 if (!resource) { | 297 if (item == crushed_sprite_resources_.end()) { |
| 289 // Cannot reload a resource that has not been previously loaded. | 298 // Cannot reload a resource that has not been previously loaded. |
| 290 return; | 299 return; |
| 291 } | 300 } |
| 292 SkBitmap skbitmap = | 301 SkBitmap skbitmap = |
| 293 gfx::CreateSkBitmapFromJavaBitmap(gfx::JavaBitmap(bitmap.obj())); | 302 gfx::CreateSkBitmapFromJavaBitmap(gfx::JavaBitmap(bitmap.obj())); |
| 294 resource->SetBitmap(skbitmap); | 303 item->second->SetBitmap(skbitmap); |
| 295 } | 304 } |
| 296 | 305 |
| 297 // static | 306 // static |
| 298 bool ResourceManagerImpl::RegisterResourceManager(JNIEnv* env) { | 307 bool ResourceManagerImpl::RegisterResourceManager(JNIEnv* env) { |
| 299 return RegisterNativesImpl(env); | 308 return RegisterNativesImpl(env); |
| 300 } | 309 } |
| 301 | 310 |
| 302 void ResourceManagerImpl::PreloadResourceFromJava(AndroidResourceType res_type, | 311 void ResourceManagerImpl::PreloadResourceFromJava(AndroidResourceType res_type, |
| 303 int res_id) { | 312 int res_id) { |
| 304 TRACE_EVENT2("ui", "ResourceManagerImpl::PreloadResourceFromJava", | 313 TRACE_EVENT2("ui", "ResourceManagerImpl::PreloadResourceFromJava", |
| (...skipping 17 matching lines...) Expand all Loading... |
| 322 TRACE_EVENT2("ui", | 331 TRACE_EVENT2("ui", |
| 323 "ResourceManagerImpl::RequestCrushedSpriteResourceFromJava", | 332 "ResourceManagerImpl::RequestCrushedSpriteResourceFromJava", |
| 324 "bitmap_res_id", bitmap_res_id, | 333 "bitmap_res_id", bitmap_res_id, |
| 325 "metadata_res_id", metadata_res_id); | 334 "metadata_res_id", metadata_res_id); |
| 326 Java_ResourceManager_crushedSpriteResourceRequested( | 335 Java_ResourceManager_crushedSpriteResourceRequested( |
| 327 base::android::AttachCurrentThread(), java_obj_, bitmap_res_id, | 336 base::android::AttachCurrentThread(), java_obj_, bitmap_res_id, |
| 328 metadata_res_id, reloading); | 337 metadata_res_id, reloading); |
| 329 } | 338 } |
| 330 | 339 |
| 331 } // namespace ui | 340 } // namespace ui |
| OLD | NEW |