| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 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/accelerated_widget_mac/ca_renderer_layer_tree.h" | 5 #include "ui/accelerated_widget_mac/ca_renderer_layer_tree.h" |
| 6 | 6 |
| 7 #include <AVFoundation/AVFoundation.h> | 7 #include <AVFoundation/AVFoundation.h> |
| 8 #include <CoreMedia/CoreMedia.h> | 8 #include <CoreMedia/CoreMedia.h> |
| 9 #include <CoreVideo/CoreVideo.h> | 9 #include <CoreVideo/CoreVideo.h> |
| 10 #include <GLES2/gl2extchromium.h> | 10 #include <GLES2/gl2extchromium.h> |
| 11 | 11 |
| 12 #include "base/command_line.h" | 12 #include "base/command_line.h" |
| 13 #include "base/lazy_instance.h" |
| 13 #include "base/mac/sdk_forward_declarations.h" | 14 #include "base/mac/sdk_forward_declarations.h" |
| 14 #include "base/trace_event/trace_event.h" | 15 #include "base/trace_event/trace_event.h" |
| 15 #include "third_party/skia/include/core/SkColor.h" | 16 #include "third_party/skia/include/core/SkColor.h" |
| 16 #include "ui/base/cocoa/animation_utils.h" | 17 #include "ui/base/cocoa/animation_utils.h" |
| 17 #include "ui/base/ui_base_switches.h" | 18 #include "ui/base/ui_base_switches.h" |
| 18 #include "ui/gfx/geometry/dip_util.h" | 19 #include "ui/gfx/geometry/dip_util.h" |
| 19 #include "ui/gl/ca_renderer_layer_params.h" | 20 #include "ui/gl/ca_renderer_layer_params.h" |
| 20 #include "ui/gl/gl_image_io_surface.h" | 21 #include "ui/gl/gl_image_io_surface.h" |
| 21 | 22 |
| 22 #if !defined(MAC_OS_X_VERSION_10_8) || \ | 23 #if !defined(MAC_OS_X_VERSION_10_8) || \ |
| (...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 122 LOG(ERROR) << "CVPixelBufferCreateWithIOSurface failed with " << cv_return; | 123 LOG(ERROR) << "CVPixelBufferCreateWithIOSurface failed with " << cv_return; |
| 123 return false; | 124 return false; |
| 124 } | 125 } |
| 125 | 126 |
| 126 return AVSampleBufferDisplayLayerEnqueueCVPixelBuffer(av_layer, | 127 return AVSampleBufferDisplayLayerEnqueueCVPixelBuffer(av_layer, |
| 127 cv_pixel_buffer); | 128 cv_pixel_buffer); |
| 128 } | 129 } |
| 129 | 130 |
| 130 } // namespace | 131 } // namespace |
| 131 | 132 |
| 133 class CARendererLayerTree::SolidColorContents |
| 134 : public base::RefCounted<CARendererLayerTree::SolidColorContents> { |
| 135 public: |
| 136 static scoped_refptr<SolidColorContents> Get(SkColor color); |
| 137 id GetContents() const; |
| 138 |
| 139 private: |
| 140 friend class base::RefCounted<SolidColorContents>; |
| 141 |
| 142 SolidColorContents(SkColor color, IOSurfaceRef io_surface); |
| 143 ~SolidColorContents(); |
| 144 |
| 145 SkColor color_ = 0; |
| 146 base::ScopedCFTypeRef<IOSurfaceRef> io_surface_; |
| 147 static base::LazyInstance<std::map<SkColor, SolidColorContents*>> map_; |
| 148 }; |
| 149 |
| 150 base::LazyInstance<std::map<SkColor, CARendererLayerTree::SolidColorContents*>> |
| 151 CARendererLayerTree::SolidColorContents::map_; |
| 152 |
| 153 // static |
| 154 scoped_refptr<CARendererLayerTree::SolidColorContents> |
| 155 CARendererLayerTree::SolidColorContents::Get(SkColor color) { |
| 156 const int kSolidColorContentsSize = 16; |
| 157 |
| 158 auto found = map_.Get().find(color); |
| 159 if (found != map_.Get().end()) |
| 160 return found->second; |
| 161 |
| 162 IOSurfaceRef io_surface = CreateIOSurface( |
| 163 gfx::Size(kSolidColorContentsSize, kSolidColorContentsSize), |
| 164 gfx::BufferFormat::BGRA_8888); |
| 165 if (!io_surface) |
| 166 return nullptr; |
| 167 |
| 168 size_t bytes_per_row = IOSurfaceGetBytesPerRowOfPlane(io_surface, 0); |
| 169 IOSurfaceLock(io_surface, 0, NULL); |
| 170 char* row_base_address = |
| 171 reinterpret_cast<char*>(IOSurfaceGetBaseAddress(io_surface)); |
| 172 for (int i = 0; i < kSolidColorContentsSize; ++i) { |
| 173 unsigned int* pixel = reinterpret_cast<unsigned int*>(row_base_address); |
| 174 for (int j = 0; j < kSolidColorContentsSize; ++j) |
| 175 *(pixel++) = color; |
| 176 row_base_address += bytes_per_row; |
| 177 } |
| 178 IOSurfaceUnlock(io_surface, 0, NULL); |
| 179 |
| 180 return new SolidColorContents(color, io_surface); |
| 181 } |
| 182 |
| 183 id CARendererLayerTree::SolidColorContents::GetContents() const { |
| 184 return static_cast<id>(io_surface_.get()); |
| 185 } |
| 186 |
| 187 CARendererLayerTree::SolidColorContents::SolidColorContents( |
| 188 SkColor color, |
| 189 IOSurfaceRef io_surface) |
| 190 : color_(color), io_surface_(io_surface) { |
| 191 DCHECK(map_.Get().find(color_) == map_.Get().end()); |
| 192 map_.Get()[color_] = this; |
| 193 } |
| 194 |
| 195 CARendererLayerTree::SolidColorContents::~SolidColorContents() { |
| 196 auto found = map_.Get().find(color_); |
| 197 DCHECK(found != map_.Get().end()); |
| 198 DCHECK(found->second == this); |
| 199 map_.Get().erase(color_); |
| 200 } |
| 201 |
| 132 CARendererLayerTree::CARendererLayerTree( | 202 CARendererLayerTree::CARendererLayerTree( |
| 133 bool allow_av_sample_buffer_display_layer) | 203 bool allow_av_sample_buffer_display_layer, |
| 204 bool allow_solid_color_layers) |
| 134 : allow_av_sample_buffer_display_layer_( | 205 : allow_av_sample_buffer_display_layer_( |
| 135 allow_av_sample_buffer_display_layer) {} | 206 allow_av_sample_buffer_display_layer), |
| 207 allow_solid_color_layers_(allow_solid_color_layers) {} |
| 136 CARendererLayerTree::~CARendererLayerTree() {} | 208 CARendererLayerTree::~CARendererLayerTree() {} |
| 137 | 209 |
| 138 bool CARendererLayerTree::ScheduleCALayer(const CARendererLayerParams& params) { | 210 bool CARendererLayerTree::ScheduleCALayer(const CARendererLayerParams& params) { |
| 139 // Excessive logging to debug white screens (crbug.com/583805). | 211 // Excessive logging to debug white screens (crbug.com/583805). |
| 140 // TODO(ccameron): change this back to a DLOG. | 212 // TODO(ccameron): change this back to a DLOG. |
| 141 if (has_committed_) { | 213 if (has_committed_) { |
| 142 LOG(ERROR) << "ScheduleCALayer called after CommitScheduledCALayers."; | 214 LOG(ERROR) << "ScheduleCALayer called after CommitScheduledCALayers."; |
| 143 return false; | 215 return false; |
| 144 } | 216 } |
| 145 return root_layer_.AddContentLayer(this, params); | 217 return root_layer_.AddContentLayer(this, params); |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 209 fullscreen_low_power_layer, video_layer->cv_pixel_buffer); | 281 fullscreen_low_power_layer, video_layer->cv_pixel_buffer); |
| 210 } else { | 282 } else { |
| 211 AVSampleBufferDisplayLayerEnqueueIOSurface(fullscreen_low_power_layer, | 283 AVSampleBufferDisplayLayerEnqueueIOSurface(fullscreen_low_power_layer, |
| 212 video_layer->io_surface); | 284 video_layer->io_surface); |
| 213 } | 285 } |
| 214 [fullscreen_low_power_layer setVideoGravity:AVLayerVideoGravityResize]; | 286 [fullscreen_low_power_layer setVideoGravity:AVLayerVideoGravityResize]; |
| 215 [fullscreen_low_power_layer setFrame:video_layer_frame_dip.ToCGRect()]; | 287 [fullscreen_low_power_layer setFrame:video_layer_frame_dip.ToCGRect()]; |
| 216 return true; | 288 return true; |
| 217 } | 289 } |
| 218 | 290 |
| 291 id CARendererLayerTree::ContentsForSolidColorForTesting(SkColor color) { |
| 292 return SolidColorContents::Get(color)->GetContents(); |
| 293 } |
| 219 | 294 |
| 220 CARendererLayerTree::RootLayer::RootLayer() {} | 295 CARendererLayerTree::RootLayer::RootLayer() {} |
| 221 | 296 |
| 222 // Note that for all destructors, the the CALayer will have been reset to nil if | 297 // Note that for all destructors, the the CALayer will have been reset to nil if |
| 223 // another layer has taken it. | 298 // another layer has taken it. |
| 224 CARendererLayerTree::RootLayer::~RootLayer() { | 299 CARendererLayerTree::RootLayer::~RootLayer() { |
| 225 [ca_layer removeFromSuperlayer]; | 300 [ca_layer removeFromSuperlayer]; |
| 226 } | 301 } |
| 227 | 302 |
| 228 CARendererLayerTree::ClipAndSortingLayer::ClipAndSortingLayer( | 303 CARendererLayerTree::ClipAndSortingLayer::ClipAndSortingLayer( |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 282 : io_surface(io_surface), | 357 : io_surface(io_surface), |
| 283 cv_pixel_buffer(cv_pixel_buffer), | 358 cv_pixel_buffer(cv_pixel_buffer), |
| 284 contents_rect(contents_rect), | 359 contents_rect(contents_rect), |
| 285 rect(rect), | 360 rect(rect), |
| 286 background_color(background_color), | 361 background_color(background_color), |
| 287 ca_edge_aa_mask(0), | 362 ca_edge_aa_mask(0), |
| 288 opacity(opacity), | 363 opacity(opacity), |
| 289 ca_filter(filter == GL_LINEAR ? kCAFilterLinear : kCAFilterNearest) { | 364 ca_filter(filter == GL_LINEAR ? kCAFilterLinear : kCAFilterNearest) { |
| 290 DCHECK(filter == GL_LINEAR || filter == GL_NEAREST); | 365 DCHECK(filter == GL_LINEAR || filter == GL_NEAREST); |
| 291 | 366 |
| 367 // On Mac OS Sierra, solid color layers are not color color corrected to the |
| 368 // output monitor color space, but IOSurface-backed layers are color |
| 369 // corrected. Note that this is only the case when the CALayers are shared |
| 370 // across processes. To make colors consistent across both solid color and |
| 371 // IOSurface-backed layers, use a cache of solid-color IOSurfaces as contents. |
| 372 // https://crbug.com/633805 |
| 373 if (!io_surface && !tree->allow_solid_color_layers_) { |
| 374 solid_color_contents = SolidColorContents::Get(background_color); |
| 375 ContentLayer::contents_rect = gfx::RectF(0, 0, 1, 1); |
| 376 } |
| 377 |
| 292 // Because the root layer has setGeometryFlipped:YES, there is some ambiguity | 378 // Because the root layer has setGeometryFlipped:YES, there is some ambiguity |
| 293 // about what exactly top and bottom mean. This ambiguity is resolved in | 379 // about what exactly top and bottom mean. This ambiguity is resolved in |
| 294 // different ways for solid color CALayers and for CALayers that have content | 380 // different ways for solid color CALayers and for CALayers that have content |
| 295 // (surprise!). For CALayers with IOSurface content, the top edge in the AA | 381 // (surprise!). For CALayers with IOSurface content, the top edge in the AA |
| 296 // mask refers to what appears as the bottom edge on-screen. For CALayers | 382 // mask refers to what appears as the bottom edge on-screen. For CALayers |
| 297 // without content (solid color layers), the top edge in the AA mask is the | 383 // without content (solid color layers), the top edge in the AA mask is the |
| 298 // top edge on-screen. | 384 // top edge on-screen. |
| 299 // https://crbug.com/567946 | 385 // https://crbug.com/567946 |
| 300 if (edge_aa_mask & GL_CA_LAYER_EDGE_LEFT_CHROMIUM) | 386 if (edge_aa_mask & GL_CA_LAYER_EDGE_LEFT_CHROMIUM) |
| 301 ca_edge_aa_mask |= kCALayerLeftEdge; | 387 ca_edge_aa_mask |= kCALayerLeftEdge; |
| 302 if (edge_aa_mask & GL_CA_LAYER_EDGE_RIGHT_CHROMIUM) | 388 if (edge_aa_mask & GL_CA_LAYER_EDGE_RIGHT_CHROMIUM) |
| 303 ca_edge_aa_mask |= kCALayerRightEdge; | 389 ca_edge_aa_mask |= kCALayerRightEdge; |
| 304 if (io_surface) { | 390 if (io_surface || solid_color_contents) { |
| 305 if (edge_aa_mask & GL_CA_LAYER_EDGE_TOP_CHROMIUM) | 391 if (edge_aa_mask & GL_CA_LAYER_EDGE_TOP_CHROMIUM) |
| 306 ca_edge_aa_mask |= kCALayerBottomEdge; | 392 ca_edge_aa_mask |= kCALayerBottomEdge; |
| 307 if (edge_aa_mask & GL_CA_LAYER_EDGE_BOTTOM_CHROMIUM) | 393 if (edge_aa_mask & GL_CA_LAYER_EDGE_BOTTOM_CHROMIUM) |
| 308 ca_edge_aa_mask |= kCALayerTopEdge; | 394 ca_edge_aa_mask |= kCALayerTopEdge; |
| 309 } else { | 395 } else { |
| 310 if (edge_aa_mask & GL_CA_LAYER_EDGE_TOP_CHROMIUM) | 396 if (edge_aa_mask & GL_CA_LAYER_EDGE_TOP_CHROMIUM) |
| 311 ca_edge_aa_mask |= kCALayerTopEdge; | 397 ca_edge_aa_mask |= kCALayerTopEdge; |
| 312 if (edge_aa_mask & GL_CA_LAYER_EDGE_BOTTOM_CHROMIUM) | 398 if (edge_aa_mask & GL_CA_LAYER_EDGE_BOTTOM_CHROMIUM) |
| 313 ca_edge_aa_mask |= kCALayerBottomEdge; | 399 ca_edge_aa_mask |= kCALayerBottomEdge; |
| 314 } | 400 } |
| 315 | 401 |
| 316 // Only allow 4:2:0 frames which fill the layer's contents to be promoted to | 402 // Only allow 4:2:0 frames which fill the layer's contents to be promoted to |
| 317 // AV layers. | 403 // AV layers. |
| 318 if (tree->allow_av_sample_buffer_display_layer_ && | 404 if (tree->allow_av_sample_buffer_display_layer_ && |
| 319 IOSurfaceGetPixelFormat(io_surface) == | 405 IOSurfaceGetPixelFormat(io_surface) == |
| 320 kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange && | 406 kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange && |
| 321 contents_rect == gfx::RectF(0, 0, 1, 1)) { | 407 contents_rect == gfx::RectF(0, 0, 1, 1)) { |
| 322 use_av_layer = true; | 408 use_av_layer = true; |
| 323 } | 409 } |
| 324 } | 410 } |
| 325 | 411 |
| 326 CARendererLayerTree::ContentLayer::ContentLayer(ContentLayer&& layer) | 412 CARendererLayerTree::ContentLayer::ContentLayer(ContentLayer&& layer) |
| 327 : io_surface(layer.io_surface), | 413 : io_surface(layer.io_surface), |
| 328 cv_pixel_buffer(layer.cv_pixel_buffer), | 414 cv_pixel_buffer(layer.cv_pixel_buffer), |
| 415 solid_color_contents(layer.solid_color_contents), |
| 329 contents_rect(layer.contents_rect), | 416 contents_rect(layer.contents_rect), |
| 330 rect(layer.rect), | 417 rect(layer.rect), |
| 331 background_color(layer.background_color), | 418 background_color(layer.background_color), |
| 332 ca_edge_aa_mask(layer.ca_edge_aa_mask), | 419 ca_edge_aa_mask(layer.ca_edge_aa_mask), |
| 333 opacity(layer.opacity), | 420 opacity(layer.opacity), |
| 334 ca_filter(layer.ca_filter), | 421 ca_filter(layer.ca_filter), |
| 335 ca_layer(std::move(layer.ca_layer)), | 422 ca_layer(std::move(layer.ca_layer)), |
| 336 av_layer(std::move(layer.av_layer)), | 423 av_layer(std::move(layer.av_layer)), |
| 337 use_av_layer(layer.use_av_layer) { | 424 use_av_layer(layer.use_av_layer) { |
| 338 DCHECK(!layer.ca_layer); | 425 DCHECK(!layer.ca_layer); |
| (...skipping 202 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 541 bool update_rect = true; | 628 bool update_rect = true; |
| 542 bool update_background_color = true; | 629 bool update_background_color = true; |
| 543 bool update_ca_edge_aa_mask = true; | 630 bool update_ca_edge_aa_mask = true; |
| 544 bool update_opacity = true; | 631 bool update_opacity = true; |
| 545 bool update_ca_filter = true; | 632 bool update_ca_filter = true; |
| 546 if (old_layer && old_layer->use_av_layer == use_av_layer) { | 633 if (old_layer && old_layer->use_av_layer == use_av_layer) { |
| 547 DCHECK(old_layer->ca_layer); | 634 DCHECK(old_layer->ca_layer); |
| 548 std::swap(ca_layer, old_layer->ca_layer); | 635 std::swap(ca_layer, old_layer->ca_layer); |
| 549 std::swap(av_layer, old_layer->av_layer); | 636 std::swap(av_layer, old_layer->av_layer); |
| 550 update_contents = old_layer->io_surface != io_surface || | 637 update_contents = old_layer->io_surface != io_surface || |
| 551 old_layer->cv_pixel_buffer != cv_pixel_buffer; | 638 old_layer->cv_pixel_buffer != cv_pixel_buffer || |
| 639 old_layer->solid_color_contents != solid_color_contents; |
| 552 update_contents_rect = old_layer->contents_rect != contents_rect; | 640 update_contents_rect = old_layer->contents_rect != contents_rect; |
| 553 update_rect = old_layer->rect != rect; | 641 update_rect = old_layer->rect != rect; |
| 554 update_background_color = old_layer->background_color != background_color; | 642 update_background_color = old_layer->background_color != background_color; |
| 555 update_ca_edge_aa_mask = old_layer->ca_edge_aa_mask != ca_edge_aa_mask; | 643 update_ca_edge_aa_mask = old_layer->ca_edge_aa_mask != ca_edge_aa_mask; |
| 556 update_opacity = old_layer->opacity != opacity; | 644 update_opacity = old_layer->opacity != opacity; |
| 557 update_ca_filter = old_layer->ca_filter != ca_filter; | 645 update_ca_filter = old_layer->ca_filter != ca_filter; |
| 558 } else { | 646 } else { |
| 559 if (use_av_layer) { | 647 if (use_av_layer) { |
| 560 av_layer.reset([[AVSampleBufferDisplayLayer alloc] init]); | 648 av_layer.reset([[AVSampleBufferDisplayLayer alloc] init]); |
| 561 ca_layer.reset([av_layer retain]); | 649 ca_layer.reset([av_layer retain]); |
| (...skipping 16 matching lines...) Expand all Loading... |
| 578 if (update_contents) { | 666 if (update_contents) { |
| 579 if (cv_pixel_buffer) { | 667 if (cv_pixel_buffer) { |
| 580 AVSampleBufferDisplayLayerEnqueueCVPixelBuffer(av_layer, | 668 AVSampleBufferDisplayLayerEnqueueCVPixelBuffer(av_layer, |
| 581 cv_pixel_buffer); | 669 cv_pixel_buffer); |
| 582 } else { | 670 } else { |
| 583 AVSampleBufferDisplayLayerEnqueueIOSurface(av_layer, io_surface); | 671 AVSampleBufferDisplayLayerEnqueueIOSurface(av_layer, io_surface); |
| 584 } | 672 } |
| 585 } | 673 } |
| 586 } else { | 674 } else { |
| 587 if (update_contents) { | 675 if (update_contents) { |
| 588 [ca_layer setContents:static_cast<id>(io_surface.get())]; | 676 if (io_surface) { |
| 677 [ca_layer setContents:static_cast<id>(io_surface.get())]; |
| 678 } else if (solid_color_contents) { |
| 679 [ca_layer setContents:solid_color_contents->GetContents()]; |
| 680 } else { |
| 681 [ca_layer setContents:nil]; |
| 682 } |
| 589 if ([ca_layer respondsToSelector:(@selector(setContentsScale:))]) | 683 if ([ca_layer respondsToSelector:(@selector(setContentsScale:))]) |
| 590 [ca_layer setContentsScale:scale_factor]; | 684 [ca_layer setContentsScale:scale_factor]; |
| 591 } | 685 } |
| 592 if (update_contents_rect) | 686 if (update_contents_rect) |
| 593 [ca_layer setContentsRect:contents_rect.ToCGRect()]; | 687 [ca_layer setContentsRect:contents_rect.ToCGRect()]; |
| 594 } | 688 } |
| 595 if (update_rect) { | 689 if (update_rect) { |
| 596 gfx::RectF dip_rect = gfx::RectF(rect); | 690 gfx::RectF dip_rect = gfx::RectF(rect); |
| 597 dip_rect.Scale(1 / scale_factor); | 691 dip_rect.Scale(1 / scale_factor); |
| 598 [ca_layer setPosition:CGPointMake(dip_rect.x(), dip_rect.y())]; | 692 [ca_layer setPosition:CGPointMake(dip_rect.x(), dip_rect.y())]; |
| (...skipping 20 matching lines...) Expand all Loading... |
| 619 } | 713 } |
| 620 | 714 |
| 621 static bool show_borders = base::CommandLine::ForCurrentProcess()->HasSwitch( | 715 static bool show_borders = base::CommandLine::ForCurrentProcess()->HasSwitch( |
| 622 switches::kShowMacOverlayBorders); | 716 switches::kShowMacOverlayBorders); |
| 623 if (show_borders) { | 717 if (show_borders) { |
| 624 base::ScopedCFTypeRef<CGColorRef> color; | 718 base::ScopedCFTypeRef<CGColorRef> color; |
| 625 if (update_anything) { | 719 if (update_anything) { |
| 626 if (use_av_layer) { | 720 if (use_av_layer) { |
| 627 // Yellow represents an AV layer that changed this frame. | 721 // Yellow represents an AV layer that changed this frame. |
| 628 color.reset(CGColorCreateGenericRGB(1, 1, 0, 1)); | 722 color.reset(CGColorCreateGenericRGB(1, 1, 0, 1)); |
| 723 } else if (io_surface) { |
| 724 // Magenta represents a CALayer that changed this frame. |
| 725 color.reset(CGColorCreateGenericRGB(1, 0, 1, 1)); |
| 726 } else if (solid_color_contents) { |
| 727 // Cyan represents a solid color IOSurface-backed layer. |
| 728 color.reset(CGColorCreateGenericRGB(0, 1, 1, 1)); |
| 629 } else { | 729 } else { |
| 630 // Pink represents a CALayer that changed this frame. | 730 // Red represents a solid color layer. |
| 631 color.reset(CGColorCreateGenericRGB(1, 0, 1, 1)); | 731 color.reset(CGColorCreateGenericRGB(1, 0, 0, 1)); |
| 632 } | 732 } |
| 633 } else { | 733 } else { |
| 634 // Grey represents a CALayer that has not changed. | 734 // Grey represents a CALayer that has not changed. |
| 635 color.reset(CGColorCreateGenericRGB(0.5, 0.5, 0.5, 1)); | 735 color.reset(CGColorCreateGenericRGB(0.5, 0.5, 0.5, 1)); |
| 636 } | 736 } |
| 637 [ca_layer setBorderWidth:1]; | 737 [ca_layer setBorderWidth:1]; |
| 638 [ca_layer setBorderColor:color]; | 738 [ca_layer setBorderColor:color]; |
| 639 } | 739 } |
| 640 } | 740 } |
| 641 | 741 |
| 642 } // namespace ui | 742 } // namespace ui |
| OLD | NEW |