diff options
author | Jocelyn Turcotte <jocelyn.turcotte@digia.com> | 2014-08-08 14:30:41 +0200 |
---|---|---|
committer | Jocelyn Turcotte <jocelyn.turcotte@digia.com> | 2014-08-12 13:49:54 +0200 |
commit | ab0a50979b9eb4dfa3320eff7e187e41efedf7a9 (patch) | |
tree | 498dfb8a97ff3361a9f7486863a52bb4e26bb898 /chromium/ui/gfx | |
parent | 4ce69f7403811819800e7c5ae1318b2647e778d1 (diff) |
Update Chromium to beta version 37.0.2062.68
Change-Id: I188e3b5aff1bec75566014291b654eb19f5bc8ca
Reviewed-by: Andras Becsi <andras.becsi@digia.com>
Diffstat (limited to 'chromium/ui/gfx')
275 files changed, 13770 insertions, 9415 deletions
diff --git a/chromium/ui/gfx/BUILD.gn b/chromium/ui/gfx/BUILD.gn new file mode 100644 index 00000000000..e2796a1b7ac --- /dev/null +++ b/chromium/ui/gfx/BUILD.gn @@ -0,0 +1,445 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//build/config/ui.gni") + +if (is_android) { + import("//build/config/android/config.gni") + import("//build/config/android/rules.gni") +} +if (use_ozone) { + import("//ui/ozone/ozone.gni") +} + +# Several targets want to include this header file, and some of them are +# child dependencies of "gfx". Therefore, we separate it out here so multiple +# targets can all have a dependency for header checking purposes without +# creating circular dependencies. +source_set("gfx_export") { + sources = [ + "gfx_export.h", + ] +} + +component("gfx") { + sources = [ + "android/device_display_info.cc", + "android/device_display_info.h", + "android/gfx_jni_registrar.cc", + "android/gfx_jni_registrar.h", + "android/java_bitmap.cc", + "android/java_bitmap.h", + "android/scroller.cc", + "android/scroller.h", + "android/shared_device_display_info.cc", + "android/shared_device_display_info.h", + "android/view_configuration.cc", + "android/view_configuration.h", + "animation/animation.cc", + "animation/animation.h", + "animation/animation_container.cc", + "animation/animation_container.h", + "animation/animation_container_element.h", + "animation/animation_container_observer.h", + "animation/animation_delegate.h", + "animation/linear_animation.cc", + "animation/linear_animation.h", + "animation/multi_animation.cc", + "animation/multi_animation.h", + "animation/slide_animation.cc", + "animation/slide_animation.h", + "animation/throb_animation.cc", + "animation/throb_animation.h", + "animation/tween.cc", + "animation/tween.h", + "blit.cc", + "blit.h", + "break_list.h", + "canvas.cc", + "canvas.h", + "canvas_android.cc", + "canvas_paint_mac.h", + "canvas_paint_mac.mm", + "canvas_paint_win.cc", + "canvas_paint_win.h", + "canvas_skia.cc", + "canvas_skia_paint.h", + "codec/jpeg_codec.cc", + "codec/jpeg_codec.h", + "codec/png_codec.cc", + "codec/png_codec.h", + "color_analysis.cc", + "color_analysis.h", + "color_profile.cc", + "color_profile.h", + "color_profile_mac.mm", + "color_profile_win.cc", + "color_utils.cc", + "color_utils.h", + "display.cc", + "display.h", + "display_observer.cc", + "display_observer.h", + "favicon_size.cc", + "favicon_size.h", + "font.cc", + "font.h", + "font_fallback_win.cc", + "font_fallback_win.h", + "font_list.cc", + "font_list.h", + "font_list_impl.cc", + "font_list_impl.h", + "font_render_params_android.cc", + "font_render_params_linux.cc", + "font_render_params_linux.h", + "font_smoothing_win.cc", + "font_smoothing_win.h", + "frame_time.h", + "gdi_util.cc", + "gdi_util.h", + "gfx_paths.cc", + "gfx_paths.h", + "gpu_memory_buffer.cc", + "gpu_memory_buffer.h", + "icon_util.cc", + "icon_util.h", + "image/canvas_image_source.cc", + "image/canvas_image_source.h", + "image/image.cc", + "image/image.h", + "image/image_family.cc", + "image/image_family.h", + "image/image_ios.mm", + "image/image_mac.mm", + "image/image_png_rep.cc", + "image/image_png_rep.h", + "image/image_skia.cc", + "image/image_skia.h", + "image/image_skia_operations.cc", + "image/image_skia_operations.h", + "image/image_skia_rep.cc", + "image/image_skia_rep.h", + "image/image_skia_source.h", + "image/image_skia_util_ios.h", + "image/image_skia_util_ios.mm", + "image/image_skia_util_mac.h", + "image/image_skia_util_mac.mm", + "image/image_util.cc", + "image/image_util.h", + "image/image_util_ios.mm", + "interpolated_transform.cc", + "interpolated_transform.h", + "linux_font_delegate.cc", + "linux_font_delegate.h", + "mac/scoped_ns_disable_screen_updates.h", + "native_widget_types.h", + "nine_image_painter.cc", + "nine_image_painter.h", + "path.cc", + "path.h", + "path_aura.cc", + "path_win.cc", + "path_win.h", + "path_x11.cc", + "path_x11.h", + "platform_font.h", + "platform_font_android.cc", + "platform_font_ios.h", + "platform_font_ios.mm", + "platform_font_mac.h", + "platform_font_mac.mm", + "platform_font_win.cc", + "platform_font_win.h", + "range/range.cc", + "range/range.h", + "range/range_mac.mm", + "range/range_win.cc", + "scoped_canvas.h", + "scoped_cg_context_save_gstate_mac.h", + "scoped_ns_graphics_context_save_gstate_mac.h", + "scoped_ns_graphics_context_save_gstate_mac.mm", + "scoped_ui_graphics_push_context_ios.h", + "scoped_ui_graphics_push_context_ios.mm", + "screen.cc", + "screen.h", + "screen_android.cc", + "screen_aura.cc", + "screen_ios.mm", + "screen_mac.mm", + "screen_win.cc", + "screen_win.h", + "scrollbar_size.cc", + "scrollbar_size.h", + "selection_model.cc", + "selection_model.h", + "sequential_id_generator.cc", + "sequential_id_generator.h", + "shadow_value.cc", + "shadow_value.h", + "skbitmap_operations.cc", + "skbitmap_operations.h", + "skia_util.cc", + "skia_util.h", + "switches.cc", + "switches.h", + "sys_color_change_listener.cc", + "sys_color_change_listener.h", + "text_constants.h", + "text_elider.cc", + "text_elider.h", + "text_utils.cc", + "text_utils.h", + "text_utils_android.cc", + "text_utils_ios.mm", + "transform.cc", + "transform.h", + "transform_util.cc", + "transform_util.h", + "ui_gfx_exports.cc", + "utf16_indexing.cc", + "utf16_indexing.h", + "vsync_provider.h", + "win/dpi.cc", + "win/dpi.h", + "win/hwnd_util.cc", + "win/hwnd_util.h", + "win/scoped_set_map_mode.h", + "win/singleton_hwnd.cc", + "win/singleton_hwnd.h", + "win/window_impl.cc", + "win/window_impl.h", + ] + + defines = [ "GFX_IMPLEMENTATION" ] + + deps = [ + ":gfx_export", + "//base", + "//base:i18n", + "//base:base_static", + "//base/third_party/dynamic_annotations", + "//skia", + "//third_party/harfbuzz-ng", + "//third_party/icu:icui18n", + "//third_party/icu:icuuc", + "//third_party/libpng", + "//third_party/zlib", + "//ui/gfx/geometry", + ] + + # Text rendering conditions (complicated so separated out). + if (is_android || is_ios) { + # We don't support RenderText on these platforms. + } else { + # These text rendering files are supported everywhere text rendering is. + sources += [ + "render_text.cc", + "render_text.h", + "render_text_harfbuzz.cc", + "render_text_harfbuzz.h", + "text_utils_skia.cc", + ] + # These are the "native" rendering routines, only one should apply. + if (is_win) { + sources += [ "render_text_win.cc" ] + } else if (is_mac) { + sources += [ "render_text_mac.cc" ] + } else if (use_pango) { + sources += [ "render_text_pango.cc" ] + } else if (use_ozone) { + sources += [ "render_text_ozone.cc" ] + } + } + + # iOS. + if (is_ios) { + sources -= [ + "codec/jpeg_codec.cc", + "codec/jpeg_codec.h", + ] + } else { + deps += [ "//third_party:jpeg" ] + } + + # Android. + if (is_android) { + sources -= [ + "animation/throb_animation.cc", + "canvas_skia.cc", + "display_observer.cc", + "selection_model.cc", + ] + + if (use_aura) { + sources -= [ "screen_android.cc" ] + } else { + sources -= [ "path.cc" ] + } + + # TODO(GYP) re-enable when base_java exists. + #if (!is_android_webview_build) { + # deps += [ "//base:base_java" ] + #} + + deps += [ ":gfx_jni_headers" ] + libs = [ + "android", + "jnigraphics", + ] + } + + # Windows. + if (is_win) { + cflags = [ + "/wd4267", # TODO(jschuh): C4267: http://crbug.com/167187 size_t -> int. + "/wd4324", # Structure was padded due to __declspec(align()), which is + # uninteresting. + ] + } else { + sources -= [ + "gdi_util.cc", + "gdi_util.h", + "icon_util.cc", + "icon_util.h", + ] + } + + # Linux. + if (is_linux) { + configs += [ "//build/config/linux:fontconfig" ] + } + + # Ozone stuff. + if (use_ozone) { + sources += [ + "platform_font_ozone.cc", + "ozone/impl/file_surface_factory.cc", + "ozone/impl/file_surface_factory.h", + "ozone/surface_factory_ozone.cc", + "ozone/surface_factory_ozone.h", + "ozone/surface_ozone.h", + "ozone/overlay_candidates_ozone.cc", + "ozone/overlay_candidates_ozone.h", + ] + } + + if (!use_aura) { + sources -= [ + "nine_image_painter.cc", + "nine_image_painter.h", + "path_aura.cc", + "screen_aura.cc", + ] + } + + if (use_x11) { + deps += [ + ":gfx_x11", + ] + } else { + sources -= [ + "path_x11.cc", + ] + } + + if (use_pango) { + sources += [ + "pango_util.cc", + "pango_util.h", + "platform_font_pango.cc", + "platform_font_pango.h", + ] + configs += [ "//build/config/linux:pangocairo" ] + } +} + +# Looking for gfx_geometry? It's //ui/gfx/geometry:geometry + +source_set("gfx_test_support") { + sources = [ + "test/gfx_util.cc", + "test/gfx_util.h", + "test/ui_cocoa_test_helper.h", + "test/ui_cocoa_test_helper.mm", + ] + + deps = [ + "//base", + "//base/test:test_support", + "//skia", + "//testing/gtest", + ] + + if (is_ios) { + # The cocoa files don't apply to iOS. + sources -= [ + "test/ui_cocoa_test_helper.h", + "test/ui_cocoa_test_helper.mm", + ] + } +} + +test("gfx_unittests") { + sources = [ + "geometry/box_unittest.cc", + "geometry/cubic_bezier_unittest.cc", + "geometry/insets_unittest.cc", + "geometry/matrix3_unittest.cc", + "geometry/point_unittest.cc", + "geometry/point3_unittest.cc", + "geometry/quad_unittest.cc", + "geometry/rect_unittest.cc", + "geometry/safe_integer_conversions_unittest.cc", + "geometry/size_unittest.cc", + "geometry/vector2d_unittest.cc", + "geometry/vector3d_unittest.cc", + "range/range_unittest.cc", + ] + + deps = [ + ":gfx", + "//base", + "//base/test:run_all_unittests", + "//base/test:run_all_unittests", + "//testing/gtest", + "//ui/gfx/geometry", + ] +} + +if (is_android) { + generate_jni("gfx_jni_headers") { + sources = [ + "../android/java/src/org/chromium/ui/gfx/BitmapHelper.java", + "../android/java/src/org/chromium/ui/gfx/DeviceDisplayInfo.java", + "../android/java/src/org/chromium/ui/gfx/ViewConfigurationHelper.java", + ] + jni_package = "gfx" + } +} + +if (use_x11) { + component("gfx_x11") { + sources = [ + "x/x11_atom_cache.cc", + "x/x11_atom_cache.h", + "x/x11_connection.cc", + "x/x11_connection.h", + "x/x11_error_tracker.cc", + "x/x11_error_tracker.h", + "x/x11_switches.cc", + "x/x11_switches.h", + "x/x11_types.cc", + "x/x11_types.h", + ] + + defines = [ "GFX_IMPLEMENTATION" ] + configs += [ "//build/config/linux:x11" ] + + deps = [ + ":gfx_export", + "//base" + ] + } +} diff --git a/chromium/ui/gfx/DEPS b/chromium/ui/gfx/DEPS index 07a2ea9406f..758e42abb66 100644 --- a/chromium/ui/gfx/DEPS +++ b/chromium/ui/gfx/DEPS @@ -1,8 +1,6 @@ include_rules = [ "+base", - "+net", "+skia/ext", - "+third_party/angle", + "+third_party/harfbuzz-ng", "+third_party/skia", - "+ui/test/ui_unittests_resource.h", # TODO(beng): remove ] diff --git a/chromium/ui/gfx/OWNERS b/chromium/ui/gfx/OWNERS index 563c600ea4d..dad6c9a8452 100644 --- a/chromium/ui/gfx/OWNERS +++ b/chromium/ui/gfx/OWNERS @@ -7,9 +7,6 @@ danakj@chromium.org # RenderText and related classes. msw@chromium.org -# RenderText and related classes. -xji@chromium.org - # Display and related classes. per-file display*=oshima@chromium.org per-file screen*=oshima@chromium.org @@ -19,5 +16,11 @@ per-file transform*=shawnsingh@chromium.org per-file transform*=vollick@chromium.org per-file interpolated_transform*=vollick@chromium.org +# GPU memory buffer interface. +per-file gpu_memory_buffer*=reveman@chromium.org + +# R-Tree implementation. +per-file r_tree*=luken@chromium.org + # If you're doing structural changes get a review from one of the OWNERS. per-file *.gyp*=* diff --git a/chromium/ui/gfx/android/OWNERS b/chromium/ui/gfx/android/OWNERS index c87688f018a..316565b6ed3 100644 --- a/chromium/ui/gfx/android/OWNERS +++ b/chromium/ui/gfx/android/OWNERS @@ -1 +1,3 @@ -yfriedman@chromium.org +aelias@chromium.org +jdduke@chromium.org +skyostil@chromium.org diff --git a/chromium/ui/gfx/android/bitmap_config_list.h b/chromium/ui/gfx/android/bitmap_config_list.h new file mode 100644 index 00000000000..9561168f3b3 --- /dev/null +++ b/chromium/ui/gfx/android/bitmap_config_list.h @@ -0,0 +1,15 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file intentionally does not have header guards, it's included +// inside a macro to generate enum values. + +#ifndef DEFINE_BITMAP_CONFIG +#error "DEFINE_BITMAP_CONFIG should be defined before including this file" +#endif +DEFINE_BITMAP_CONFIG(FORMAT_NO_CONFIG, 0) +DEFINE_BITMAP_CONFIG(FORMAT_ALPHA_8, 1) +DEFINE_BITMAP_CONFIG(FORMAT_ARGB_4444, 2) +DEFINE_BITMAP_CONFIG(FORMAT_ARGB_8888, 3) +DEFINE_BITMAP_CONFIG(FORMAT_RGB_565, 4) diff --git a/chromium/ui/gfx/android/device_display_info.cc b/chromium/ui/gfx/android/device_display_info.cc index 2de8440e1d3..4d2c80c09d6 100644 --- a/chromium/ui/gfx/android/device_display_info.cc +++ b/chromium/ui/gfx/android/device_display_info.cc @@ -23,6 +23,14 @@ int DeviceDisplayInfo::GetDisplayWidth() { return SharedDeviceDisplayInfo::GetInstance()->GetDisplayWidth(); } +int DeviceDisplayInfo::GetPhysicalDisplayHeight() { + return SharedDeviceDisplayInfo::GetInstance()->GetPhysicalDisplayHeight(); +} + +int DeviceDisplayInfo::GetPhysicalDisplayWidth() { + return SharedDeviceDisplayInfo::GetInstance()->GetPhysicalDisplayWidth(); +} + int DeviceDisplayInfo::GetBitsPerPixel() { return SharedDeviceDisplayInfo::GetInstance()->GetBitsPerPixel(); } @@ -39,4 +47,8 @@ int DeviceDisplayInfo::GetSmallestDIPWidth() { return SharedDeviceDisplayInfo::GetInstance()->GetSmallestDIPWidth(); } +int DeviceDisplayInfo::GetRotationDegrees() { + return SharedDeviceDisplayInfo::GetInstance()->GetRotationDegrees(); +} + } // namespace gfx diff --git a/chromium/ui/gfx/android/device_display_info.h b/chromium/ui/gfx/android/device_display_info.h index 83968e9b51e..74e0ada4875 100644 --- a/chromium/ui/gfx/android/device_display_info.h +++ b/chromium/ui/gfx/android/device_display_info.h @@ -26,6 +26,18 @@ class GFX_EXPORT DeviceDisplayInfo { // Returns display width in physical pixels. int GetDisplayWidth(); + // Returns real display height in physical pixels. + // This version does not subtract window decorations etc. + // WARNING: This is only supported on JB-MR1 (sdk >= 17). Either + // check the SDK-level, or check for '0' being returned. + int GetPhysicalDisplayHeight(); + + // Returns real display width in physical pixels. + // This version does not subtract window decorations etc. + // WARNING: This is only supported on JB-MR1 (sdk >= 17). Either + // check the SDK-level, or check for '0' being returned. + int GetPhysicalDisplayWidth(); + // Returns number of bits per pixel. int GetBitsPerPixel(); @@ -39,6 +51,11 @@ class GFX_EXPORT DeviceDisplayInfo { // Smallest possible screen size in density-independent pixels. int GetSmallestDIPWidth(); + // Returns the display rotation angle from its natural orientation. Expected + // values are one of { 0, 90, 180, 270 }. + // See DeviceDispayInfo.java for more information. + int GetRotationDegrees(); + private: DISALLOW_COPY_AND_ASSIGN(DeviceDisplayInfo); }; diff --git a/chromium/ui/gfx/android/java_bitmap.cc b/chromium/ui/gfx/android/java_bitmap.cc index 2a1be65d215..a5b891a4af7 100644 --- a/chromium/ui/gfx/android/java_bitmap.cc +++ b/chromium/ui/gfx/android/java_bitmap.cc @@ -9,11 +9,10 @@ #include "base/android/jni_string.h" #include "base/logging.h" #include "jni/BitmapHelper_jni.h" -#include "skia/ext/image_operations.h" -#include "third_party/skia/include/core/SkBitmap.h" #include "ui/gfx/size.h" using base::android::AttachCurrentThread; +using base::android::ConvertUTF8ToJavaString; namespace gfx { @@ -42,17 +41,52 @@ bool JavaBitmap::RegisterJavaBitmap(JNIEnv* env) { return RegisterNativesImpl(env); } -static ScopedJavaLocalRef<jobject> CreateJavaBitmap(const gfx::Size& size) { - return Java_BitmapHelper_createBitmap(AttachCurrentThread(), - size.width(), size.height()); +static int SkBitmapConfigToBitmapFormat(SkBitmap::Config bitmap_config) { + switch (bitmap_config) { + case SkBitmap::kA8_Config: + return BITMAP_FORMAT_ALPHA_8; + case SkBitmap::kARGB_4444_Config: + return BITMAP_FORMAT_ARGB_4444; + case SkBitmap::kARGB_8888_Config: + return BITMAP_FORMAT_ARGB_8888; + case SkBitmap::kRGB_565_Config: + return BITMAP_FORMAT_RGB_565; + case SkBitmap::kNo_Config: + default: + NOTREACHED(); + return BITMAP_FORMAT_NO_CONFIG; + } +} + +ScopedJavaLocalRef<jobject> CreateJavaBitmap(int width, + int height, + SkBitmap::Config bitmap_config) { + DCHECK_GT(width, 0); + DCHECK_GT(height, 0); + int java_bitmap_config = SkBitmapConfigToBitmapFormat(bitmap_config); + return Java_BitmapHelper_createBitmap( + AttachCurrentThread(), width, height, java_bitmap_config); +} + +ScopedJavaLocalRef<jobject> CreateJavaBitmapFromAndroidResource( + const char* name, + gfx::Size size) { + DCHECK(name); + DCHECK(!size.IsEmpty()); + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef<jstring> jname(ConvertUTF8ToJavaString(env, name)); + return Java_BitmapHelper_decodeDrawableResource( + env, jname.obj(), size.width(), size.height()); } ScopedJavaLocalRef<jobject> ConvertToJavaBitmap(const SkBitmap* skbitmap) { DCHECK(skbitmap); - DCHECK_EQ(skbitmap->bytesPerPixel(), 4); - - ScopedJavaLocalRef<jobject> jbitmap = - CreateJavaBitmap(gfx::Size(skbitmap->width(), skbitmap->height())); + DCHECK(!skbitmap->isNull()); + SkBitmap::Config bitmap_config = skbitmap->config(); + DCHECK((bitmap_config == SkBitmap::kRGB_565_Config) || + (bitmap_config == SkBitmap::kARGB_8888_Config)); + ScopedJavaLocalRef<jobject> jbitmap = CreateJavaBitmap( + skbitmap->width(), skbitmap->height(), bitmap_config); SkAutoLockPixels src_lock(*skbitmap); JavaBitmap dst_lock(jbitmap.obj()); void* src_pixels = skbitmap->getPixels(); @@ -62,8 +96,13 @@ ScopedJavaLocalRef<jobject> ConvertToJavaBitmap(const SkBitmap* skbitmap) { return jbitmap; } -SkBitmap CreateSkBitmapFromJavaBitmap(JavaBitmap& jbitmap) { - DCHECK_EQ(jbitmap.format(), ANDROID_BITMAP_FORMAT_RGBA_8888); +SkBitmap CreateSkBitmapFromJavaBitmap(const JavaBitmap& jbitmap) { + // TODO(jdduke): Convert to DCHECK's when sufficient data has been capture for + // crbug.com/341406. + CHECK_EQ(jbitmap.format(), ANDROID_BITMAP_FORMAT_RGBA_8888); + CHECK(!jbitmap.size().IsEmpty()); + CHECK_GT(jbitmap.stride(), 0U); + CHECK(jbitmap.pixels()); gfx::Size src_size = jbitmap.size(); @@ -72,30 +111,34 @@ SkBitmap CreateSkBitmapFromJavaBitmap(JavaBitmap& jbitmap) { src_size.width(), src_size.height(), jbitmap.stride()); - skbitmap.allocPixels(); + if (!skbitmap.allocPixels()) { + LOG(FATAL) << " Failed to allocate bitmap of size " << src_size.width() + << "x" << src_size.height() << " stride=" << jbitmap.stride(); + } SkAutoLockPixels dst_lock(skbitmap); - - void* src_pixels = jbitmap.pixels(); + const void* src_pixels = jbitmap.pixels(); void* dst_pixels = skbitmap.getPixels(); - memcpy(dst_pixels, src_pixels, skbitmap.getSize()); return skbitmap; } -SkBitmap CreateSkBitmapFromResource(const char* name, gfx::Size size) { - DCHECK(!size.IsEmpty()); - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef<jstring> jname(env, env->NewStringUTF(name)); - ScopedJavaLocalRef<jobject> jobj(Java_BitmapHelper_decodeDrawableResource( - env, jname.obj(), size.width(), size.height())); - if (jobj.is_null()) - return SkBitmap(); - - JavaBitmap jbitmap(jobj.obj()); - SkBitmap bitmap = CreateSkBitmapFromJavaBitmap(jbitmap); - return skia::ImageOperations::Resize( - bitmap, skia::ImageOperations::RESIZE_BOX, size.width(), size.height()); +SkBitmap::Config ConvertToSkiaConfig(jobject bitmap_config) { + int jbitmap_config = Java_BitmapHelper_getBitmapFormatForConfig( + AttachCurrentThread(), bitmap_config); + switch (jbitmap_config) { + case BITMAP_FORMAT_ALPHA_8: + return SkBitmap::kA8_Config; + case BITMAP_FORMAT_ARGB_4444: + return SkBitmap::kARGB_4444_Config; + case BITMAP_FORMAT_ARGB_8888: + return SkBitmap::kARGB_8888_Config; + case BITMAP_FORMAT_RGB_565: + return SkBitmap::kRGB_565_Config; + case BITMAP_FORMAT_NO_CONFIG: + default: + return SkBitmap::kNo_Config; + } } } // namespace gfx diff --git a/chromium/ui/gfx/android/java_bitmap.h b/chromium/ui/gfx/android/java_bitmap.h index 9d1e4432cb7..44a17cade74 100644 --- a/chromium/ui/gfx/android/java_bitmap.h +++ b/chromium/ui/gfx/android/java_bitmap.h @@ -8,12 +8,20 @@ #include <jni.h> #include "base/android/scoped_java_ref.h" +#include "third_party/skia/include/core/SkBitmap.h" #include "ui/gfx/size.h" -class SkBitmap; - namespace gfx { +// Define Bitmap Config values like BITMAP_CONFIG_ARGB_8888 in a +// way that ensures they're always the same than their Java counterpart. + +enum BitmapConfig { +#define DEFINE_BITMAP_CONFIG(x, y) BITMAP_##x = y, +#include "bitmap_config_list.h" +#undef DEFINE_BITMAP_CONFIG +}; + // This class wraps a JNI AndroidBitmap object to make it easier to use. It // handles locking and unlocking of the underlying pixels, along with wrapping // various JNI methods. @@ -23,6 +31,7 @@ class GFX_EXPORT JavaBitmap { ~JavaBitmap(); inline void* pixels() { return pixels_; } + inline const void* pixels() const { return pixels_; } inline const gfx::Size& size() const { return size_; } // Formats are in android/bitmap.h; e.g. ANDROID_BITMAP_FORMAT_RGBA_8888 inline int format() const { return format_; } @@ -41,15 +50,32 @@ class GFX_EXPORT JavaBitmap { DISALLOW_COPY_AND_ASSIGN(JavaBitmap); }; +// Allocates a Java-backed bitmap (android.graphics.Bitmap) with the given +// (non-empty!) size and configuration. +GFX_EXPORT base::android::ScopedJavaLocalRef<jobject> CreateJavaBitmap( + int width, + int height, + SkBitmap::Config bitmap_config); + +// Loads a Java-backed bitmap (android.graphics.Bitmap) from the provided +// drawable resource identifier (e.g., android:drawable/overscroll_glow). If the +// resource loads successfully, it will be integrally scaled down, preserving +// aspect ratio, to a size no smaller than |size|. Otherwise, null is returned. +GFX_EXPORT base::android::ScopedJavaLocalRef<jobject> + CreateJavaBitmapFromAndroidResource(const char* name, gfx::Size size); + +// Converts |skbitmap| to a Java-backed bitmap (android.graphics.Bitmap). +// Note: |skbitmap| is assumed to be non-null, non-empty and one of RGBA_8888 or +// RGB_565 formats. GFX_EXPORT base::android::ScopedJavaLocalRef<jobject> ConvertToJavaBitmap( const SkBitmap* skbitmap); -GFX_EXPORT SkBitmap CreateSkBitmapFromJavaBitmap(JavaBitmap& jbitmap); +// Converts |bitmap| to an SkBitmap of the same size and format. +// Note: |jbitmap| is assumed to be non-null, non-empty and of format RGBA_8888. +GFX_EXPORT SkBitmap CreateSkBitmapFromJavaBitmap(const JavaBitmap& jbitmap); -// If the resource loads successfully, it will be resized to |size|. -// Note: If the source resource is smaller than |size|, quality may suffer. -GFX_EXPORT SkBitmap CreateSkBitmapFromResource(const char* name, - gfx::Size size); +// Returns a Skia config value for the requested input java Bitmap.Config. +GFX_EXPORT SkBitmap::Config ConvertToSkiaConfig(jobject bitmap_config); } // namespace gfx diff --git a/chromium/ui/gfx/android/scroller.cc b/chromium/ui/gfx/android/scroller.cc new file mode 100644 index 00000000000..4a44741f52e --- /dev/null +++ b/chromium/ui/gfx/android/scroller.cc @@ -0,0 +1,439 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/gfx/android/scroller.h" + +#include <cmath> + +#include "base/lazy_instance.h" + +namespace gfx { +namespace { + +// Default scroll duration from android.widget.Scroller. +const int kDefaultDurationMs = 250; + +// Default friction constant in android.view.ViewConfiguration. +const float kDefaultFriction = 0.015f; + +// == std::log(0.78f) / std::log(0.9f) +const float kDecelerationRate = 2.3582018f; + +// Tension lines cross at (kInflexion, 1). +const float kInflexion = 0.35f; + +const float kEpsilon = 1e-5f; + +bool ApproxEquals(float a, float b) { + return std::abs(a - b) < kEpsilon; +} + +struct ViscosityConstants { + ViscosityConstants() + : viscous_fluid_scale_(8.f), viscous_fluid_normalize_(1.f) { + viscous_fluid_normalize_ = 1.0f / ApplyViscosity(1.0f); + } + + float ApplyViscosity(float x) { + x *= viscous_fluid_scale_; + if (x < 1.0f) { + x -= (1.0f - std::exp(-x)); + } else { + float start = 0.36787944117f; // 1/e == exp(-1) + x = 1.0f - std::exp(1.0f - x); + x = start + x * (1.0f - start); + } + x *= viscous_fluid_normalize_; + return x; + } + + private: + // This controls the intensity of the viscous fluid effect. + float viscous_fluid_scale_; + float viscous_fluid_normalize_; + + DISALLOW_COPY_AND_ASSIGN(ViscosityConstants); +}; + +struct SplineConstants { + SplineConstants() { + const float kStartTension = 0.5f; + const float kEndTension = 1.0f; + const float kP1 = kStartTension * kInflexion; + const float kP2 = 1.0f - kEndTension * (1.0f - kInflexion); + + float x_min = 0.0f; + float y_min = 0.0f; + for (int i = 0; i < NUM_SAMPLES; i++) { + const float alpha = static_cast<float>(i) / NUM_SAMPLES; + + float x_max = 1.0f; + float x, tx, coef; + while (true) { + x = x_min + (x_max - x_min) / 2.0f; + coef = 3.0f * x * (1.0f - x); + tx = coef * ((1.0f - x) * kP1 + x * kP2) + x * x * x; + if (ApproxEquals(tx, alpha)) + break; + if (tx > alpha) + x_max = x; + else + x_min = x; + } + spline_position_[i] = coef * ((1.0f - x) * kStartTension + x) + x * x * x; + + float y_max = 1.0f; + float y, dy; + while (true) { + y = y_min + (y_max - y_min) / 2.0f; + coef = 3.0f * y * (1.0f - y); + dy = coef * ((1.0f - y) * kStartTension + y) + y * y * y; + if (ApproxEquals(dy, alpha)) + break; + if (dy > alpha) + y_max = y; + else + y_min = y; + } + spline_time_[i] = coef * ((1.0f - y) * kP1 + y * kP2) + y * y * y; + } + spline_position_[NUM_SAMPLES] = spline_time_[NUM_SAMPLES] = 1.0f; + } + + void CalculateCoefficients(float t, + float* distance_coef, + float* velocity_coef) { + *distance_coef = 1.f; + *velocity_coef = 0.f; + const int index = static_cast<int>(NUM_SAMPLES * t); + if (index < NUM_SAMPLES) { + const float t_inf = static_cast<float>(index) / NUM_SAMPLES; + const float t_sup = static_cast<float>(index + 1) / NUM_SAMPLES; + const float d_inf = spline_position_[index]; + const float d_sup = spline_position_[index + 1]; + *velocity_coef = (d_sup - d_inf) / (t_sup - t_inf); + *distance_coef = d_inf + (t - t_inf) * *velocity_coef; + } + } + + private: + enum { + NUM_SAMPLES = 100 + }; + + float spline_position_[NUM_SAMPLES + 1]; + float spline_time_[NUM_SAMPLES + 1]; + + DISALLOW_COPY_AND_ASSIGN(SplineConstants); +}; + +float ComputeDeceleration(float friction) { + const float kGravityEarth = 9.80665f; + return kGravityEarth // g (m/s^2) + * 39.37f // inch/meter + * 160.f // pixels/inch + * friction; +} + +template <typename T> +int Signum(T t) { + return (T(0) < t) - (t < T(0)); +} + +template <typename T> +T Clamped(T t, T a, T b) { + return t < a ? a : (t > b ? b : t); +} + +// Leaky to allow access from the impl thread. +base::LazyInstance<ViscosityConstants>::Leaky g_viscosity_constants = + LAZY_INSTANCE_INITIALIZER; + +base::LazyInstance<SplineConstants>::Leaky g_spline_constants = + LAZY_INSTANCE_INITIALIZER; + +} // namespace + +Scroller::Config::Config() + : fling_friction(kDefaultFriction), + flywheel_enabled(false) {} + +Scroller::Scroller(const Config& config) + : mode_(UNDEFINED), + start_x_(0), + start_y_(0), + final_x_(0), + final_y_(0), + min_x_(0), + max_x_(0), + min_y_(0), + max_y_(0), + curr_x_(0), + curr_y_(0), + duration_seconds_reciprocal_(1), + delta_x_(0), + delta_x_norm_(1), + delta_y_(0), + delta_y_norm_(1), + finished_(true), + flywheel_enabled_(config.flywheel_enabled), + velocity_(0), + curr_velocity_(0), + distance_(0), + fling_friction_(config.fling_friction), + deceleration_(ComputeDeceleration(fling_friction_)), + tuning_coeff_(ComputeDeceleration(0.84f)) {} + +Scroller::~Scroller() {} + +void Scroller::StartScroll(float start_x, + float start_y, + float dx, + float dy, + base::TimeTicks start_time) { + StartScroll(start_x, + start_y, + dx, + dy, + start_time, + base::TimeDelta::FromMilliseconds(kDefaultDurationMs)); +} + +void Scroller::StartScroll(float start_x, + float start_y, + float dx, + float dy, + base::TimeTicks start_time, + base::TimeDelta duration) { + mode_ = SCROLL_MODE; + finished_ = false; + duration_ = duration; + duration_seconds_reciprocal_ = 1.0 / duration_.InSecondsF(); + start_time_ = start_time; + curr_x_ = start_x_ = start_x; + curr_y_ = start_y_ = start_y; + final_x_ = start_x + dx; + final_y_ = start_y + dy; + RecomputeDeltas(); + curr_time_ = start_time_; +} + +void Scroller::Fling(float start_x, + float start_y, + float velocity_x, + float velocity_y, + float min_x, + float max_x, + float min_y, + float max_y, + base::TimeTicks start_time) { + // Continue a scroll or fling in progress. + if (flywheel_enabled_ && !finished_) { + float old_velocity_x = GetCurrVelocityX(); + float old_velocity_y = GetCurrVelocityY(); + if (Signum(velocity_x) == Signum(old_velocity_x) && + Signum(velocity_y) == Signum(old_velocity_y)) { + velocity_x += old_velocity_x; + velocity_y += old_velocity_y; + } + } + + mode_ = FLING_MODE; + finished_ = false; + + float velocity = std::sqrt(velocity_x * velocity_x + velocity_y * velocity_y); + + velocity_ = velocity; + duration_ = GetSplineFlingDuration(velocity); + duration_seconds_reciprocal_ = 1.0 / duration_.InSecondsF(); + start_time_ = start_time; + curr_time_ = start_time_; + curr_x_ = start_x_ = start_x; + curr_y_ = start_y_ = start_y; + + float coeff_x = velocity == 0 ? 1.0f : velocity_x / velocity; + float coeff_y = velocity == 0 ? 1.0f : velocity_y / velocity; + + double total_distance = GetSplineFlingDistance(velocity); + distance_ = total_distance * Signum(velocity); + + min_x_ = min_x; + max_x_ = max_x; + min_y_ = min_y; + max_y_ = max_y; + + final_x_ = start_x + total_distance * coeff_x; + final_x_ = Clamped(final_x_, min_x_, max_x_); + + final_y_ = start_y + total_distance * coeff_y; + final_y_ = Clamped(final_y_, min_y_, max_y_); + + RecomputeDeltas(); +} + +bool Scroller::ComputeScrollOffset(base::TimeTicks time) { + if (finished_) + return false; + + if (time == curr_time_) + return true; + + base::TimeDelta time_passed = time - start_time_; + + if (time_passed < base::TimeDelta()) { + time_passed = base::TimeDelta(); + } + + if (time_passed >= duration_) { + curr_x_ = final_x_; + curr_y_ = final_y_; + curr_time_ = start_time_ + duration_; + finished_ = true; + return true; + } + + curr_time_ = time; + + const float t = time_passed.InSecondsF() * duration_seconds_reciprocal_; + + switch (mode_) { + case UNDEFINED: + NOTREACHED() << "|StartScroll()| or |Fling()| must be called prior to " + "scroll offset computation."; + return false; + + case SCROLL_MODE: { + float x = g_viscosity_constants.Get().ApplyViscosity(t); + + curr_x_ = start_x_ + x * delta_x_; + curr_y_ = start_y_ + x * delta_y_; + } break; + + case FLING_MODE: { + float distance_coef = 1.f; + float velocity_coef = 0.f; + g_spline_constants.Get().CalculateCoefficients( + t, &distance_coef, &velocity_coef); + + curr_velocity_ = velocity_coef * distance_ * duration_seconds_reciprocal_; + + curr_x_ = start_x_ + distance_coef * delta_x_; + curr_x_ = Clamped(curr_x_, min_x_, max_x_); + + curr_y_ = start_y_ + distance_coef * delta_y_; + curr_y_ = Clamped(curr_y_, min_y_, max_y_); + + if (ApproxEquals(curr_x_, final_x_) && ApproxEquals(curr_y_, final_y_)) { + finished_ = true; + } + } break; + } + + return true; +} + +void Scroller::ExtendDuration(base::TimeDelta extend) { + base::TimeDelta passed = GetTimePassed(); + duration_ = passed + extend; + duration_seconds_reciprocal_ = 1.0 / duration_.InSecondsF(); + finished_ = false; +} + +void Scroller::SetFinalX(float new_x) { + final_x_ = new_x; + finished_ = false; + RecomputeDeltas(); +} + +void Scroller::SetFinalY(float new_y) { + final_y_ = new_y; + finished_ = false; + RecomputeDeltas(); +} + +void Scroller::AbortAnimation() { + curr_x_ = final_x_; + curr_y_ = final_y_; + curr_velocity_ = 0; + curr_time_ = start_time_ + duration_; + finished_ = true; +} + +void Scroller::ForceFinished(bool finished) { finished_ = finished; } + +bool Scroller::IsFinished() const { return finished_; } + +base::TimeDelta Scroller::GetTimePassed() const { + return curr_time_ - start_time_; +} + +base::TimeDelta Scroller::GetDuration() const { return duration_; } + +float Scroller::GetCurrX() const { return curr_x_; } + +float Scroller::GetCurrY() const { return curr_y_; } + +float Scroller::GetCurrVelocity() const { + if (finished_) + return 0; + if (mode_ == FLING_MODE) + return curr_velocity_; + return velocity_ - deceleration_ * GetTimePassed().InSecondsF() * 0.5f; +} + +float Scroller::GetCurrVelocityX() const { + return delta_x_norm_ * GetCurrVelocity(); +} + +float Scroller::GetCurrVelocityY() const { + return delta_y_norm_ * GetCurrVelocity(); +} + +float Scroller::GetStartX() const { return start_x_; } + +float Scroller::GetStartY() const { return start_y_; } + +float Scroller::GetFinalX() const { return final_x_; } + +float Scroller::GetFinalY() const { return final_y_; } + +bool Scroller::IsScrollingInDirection(float xvel, float yvel) const { + return !finished_ && Signum(xvel) == Signum(delta_x_) && + Signum(yvel) == Signum(delta_y_); +} + +void Scroller::RecomputeDeltas() { + delta_x_ = final_x_ - start_x_; + delta_y_ = final_y_ - start_y_; + + const float hyp = std::sqrt(delta_x_ * delta_x_ + delta_y_ * delta_y_); + if (hyp > kEpsilon) { + delta_x_norm_ = delta_x_ / hyp; + delta_y_norm_ = delta_y_ / hyp; + } else { + delta_x_norm_ = delta_y_norm_ = 1; + } +} + +double Scroller::GetSplineDeceleration(float velocity) const { + return std::log(kInflexion * std::abs(velocity) / + (fling_friction_ * tuning_coeff_)); +} + +base::TimeDelta Scroller::GetSplineFlingDuration(float velocity) const { + const double l = GetSplineDeceleration(velocity); + const double decel_minus_one = kDecelerationRate - 1.0; + const double time_seconds = std::exp(l / decel_minus_one); + return base::TimeDelta::FromMicroseconds(time_seconds * + base::Time::kMicrosecondsPerSecond); +} + +double Scroller::GetSplineFlingDistance(float velocity) const { + const double l = GetSplineDeceleration(velocity); + const double decel_minus_one = kDecelerationRate - 1.0; + return fling_friction_ * tuning_coeff_ * + std::exp(kDecelerationRate / decel_minus_one * l); +} + +} // namespace gfx diff --git a/chromium/ui/gfx/android/scroller.h b/chromium/ui/gfx/android/scroller.h new file mode 100644 index 00000000000..39d7255dc87 --- /dev/null +++ b/chromium/ui/gfx/android/scroller.h @@ -0,0 +1,148 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_GFX_ANDROID_SCROLLER_H_ +#define UI_GFX_ANDROID_SCROLLER_H_ + +#include "base/time/time.h" +#include "ui/gfx/gfx_export.h" + +namespace gfx { + +// Native port of android.widget.Scroller. +// * Change-Id: I4365946f890a76fcfa78ca9d69f2a8e0848095a9 +// * Please update the Change-Id as upstream Android changes are pulled. +class GFX_EXPORT Scroller { + public: + struct Config { + Config(); + + // Controls fling deceleration. Defaults to 0.015f. + float fling_friction; + + // Controls fling accumulation. Defaults to disabled. + bool flywheel_enabled; + }; + + explicit Scroller(const Config& config); + ~Scroller(); + + // Start scrolling by providing a starting point and the distance to travel. + // The default value of 250 milliseconds will be used for the duration. + void StartScroll(float start_x, + float start_y, + float dx, + float dy, + base::TimeTicks start_time); + + // Start scrolling by providing a starting point, the distance to travel, + // and the duration of the scroll. + void StartScroll(float start_x, + float start_y, + float dx, + float dy, + base::TimeTicks start_time, + base::TimeDelta duration); + + // Start scrolling based on a fling gesture. The distance travelled will + // depend on the initial velocity of the fling. + void Fling(float start_x, + float start_y, + float velocity_x, + float velocity_y, + float min_x, + float max_x, + float min_y, + float max_y, + base::TimeTicks start_time); + + // Call this when you want to know the new location. If it returns true, + // the animation is not yet finished. + bool ComputeScrollOffset(base::TimeTicks time); + + // Extend the scroll animation by |extend|. This allows a running animation + // to scroll further and longer when used with |SetFinalX()| or |SetFinalY()|. + void ExtendDuration(base::TimeDelta extend); + void SetFinalX(float new_x); + void SetFinalY(float new_y); + + // Stops the animation. Contrary to |ForceFinished()|, aborting the animation + // causes the scroller to move to the final x and y position. + void AbortAnimation(); + + // Terminate the scroll without affecting the current x and y positions. + void ForceFinished(bool finished); + + // Returns whether the scroller has finished scrolling. + bool IsFinished() const; + + // Returns the time elapsed since the beginning of the scrolling. + base::TimeDelta GetTimePassed() const; + + // Returns how long the scroll event will take. + base::TimeDelta GetDuration() const; + + float GetStartX() const; + float GetStartY() const; + float GetCurrX() const; + float GetCurrY() const; + float GetCurrVelocity() const; + float GetCurrVelocityX() const; + float GetCurrVelocityY() const; + float GetFinalX() const; + float GetFinalY() const; + + bool IsScrollingInDirection(float xvel, float yvel) const; + + private: + enum Mode { + UNDEFINED, + SCROLL_MODE, + FLING_MODE, + }; + + void OnDurationChanged(); + void RecomputeDeltas(); + + double GetSplineDeceleration(float velocity) const; + base::TimeDelta GetSplineFlingDuration(float velocity) const; + double GetSplineFlingDistance(float velocity) const; + + Mode mode_; + + float start_x_; + float start_y_; + float final_x_; + float final_y_; + + float min_x_; + float max_x_; + float min_y_; + float max_y_; + + float curr_x_; + float curr_y_; + base::TimeTicks start_time_; + base::TimeTicks curr_time_; + base::TimeDelta duration_; + double duration_seconds_reciprocal_; + float delta_x_; + float delta_x_norm_; + float delta_y_; + float delta_y_norm_; + bool finished_; + bool flywheel_enabled_; + + float velocity_; + float curr_velocity_; + float distance_; + + float fling_friction_; + float deceleration_; + float tuning_coeff_; +}; + +} // namespace gfx + +#endif // UI_GFX_ANDROID_SCROLLER_H_ diff --git a/chromium/ui/gfx/android/scroller_unittest.cc b/chromium/ui/gfx/android/scroller_unittest.cc new file mode 100644 index 00000000000..9b1018d6ab9 --- /dev/null +++ b/chromium/ui/gfx/android/scroller_unittest.cc @@ -0,0 +1,167 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/time/time.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/gfx/android/scroller.h" + +namespace gfx { + +namespace { + +const float kDefaultStartX = 7.f; +const float kDefaultStartY = 25.f; +const float kDefaultDeltaX = -20.f; +const float kDefaultDeltaY = 73.f; +const float kDefaultVelocityX = -35.f; +const float kDefaultVelocityY = 22.f; +const float kEpsilon = 1e-3f; + +Scroller::Config DefaultConfig() { + return Scroller::Config(); +} + +} // namespace + +class ScrollerTest : public testing::Test {}; + +TEST_F(ScrollerTest, Scroll) { + Scroller scroller(DefaultConfig()); + base::TimeTicks start_time = base::TimeTicks::Now(); + + // Start a scroll and verify initialized values. + scroller.StartScroll(kDefaultStartX, + kDefaultStartY, + kDefaultDeltaX, + kDefaultDeltaY, + start_time); + + EXPECT_EQ(kDefaultStartX, scroller.GetStartX()); + EXPECT_EQ(kDefaultStartY, scroller.GetStartY()); + EXPECT_EQ(kDefaultStartX, scroller.GetCurrX()); + EXPECT_EQ(kDefaultStartY, scroller.GetCurrY()); + EXPECT_EQ(kDefaultStartX + kDefaultDeltaX, scroller.GetFinalX()); + EXPECT_EQ(kDefaultStartY + kDefaultDeltaY, scroller.GetFinalY()); + EXPECT_FALSE(scroller.IsFinished()); + EXPECT_EQ(base::TimeDelta(), scroller.GetTimePassed()); + + // Advance halfway through the scroll. + const base::TimeDelta scroll_duration = scroller.GetDuration(); + scroller.ComputeScrollOffset(start_time + scroll_duration / 2); + + // Ensure we've moved in the direction of the delta, but have yet to reach + // the target. + EXPECT_GT(kDefaultStartX, scroller.GetCurrX()); + EXPECT_LT(kDefaultStartY, scroller.GetCurrY()); + EXPECT_LT(scroller.GetFinalX(), scroller.GetCurrX()); + EXPECT_GT(scroller.GetFinalY(), scroller.GetCurrY()); + EXPECT_FALSE(scroller.IsFinished()); + + // Ensure our velocity is non-zero and in the same direction as the delta. + EXPECT_GT(0.f, scroller.GetCurrVelocityX() * kDefaultDeltaX); + EXPECT_GT(0.f, scroller.GetCurrVelocityY() * kDefaultDeltaY); + EXPECT_TRUE(scroller.IsScrollingInDirection(kDefaultDeltaX, kDefaultDeltaY)); + + // Repeated offset computations at the same timestamp should yield identical + // results. + float curr_x = scroller.GetCurrX(); + float curr_y = scroller.GetCurrY(); + float curr_velocity_x = scroller.GetCurrVelocityX(); + float curr_velocity_y = scroller.GetCurrVelocityY(); + scroller.ComputeScrollOffset(start_time + scroll_duration / 2); + EXPECT_EQ(curr_x, scroller.GetCurrX()); + EXPECT_EQ(curr_y, scroller.GetCurrY()); + EXPECT_EQ(curr_velocity_x, scroller.GetCurrVelocityX()); + EXPECT_EQ(curr_velocity_y, scroller.GetCurrVelocityY()); + + // Advance to the end. + scroller.ComputeScrollOffset(start_time + scroll_duration); + EXPECT_EQ(scroller.GetFinalX(), scroller.GetCurrX()); + EXPECT_EQ(scroller.GetFinalY(), scroller.GetCurrY()); + EXPECT_TRUE(scroller.IsFinished()); + EXPECT_EQ(scroll_duration, scroller.GetTimePassed()); + EXPECT_NEAR(0.f, scroller.GetCurrVelocityX(), kEpsilon); + EXPECT_NEAR(0.f, scroller.GetCurrVelocityY(), kEpsilon); + + // Try to advance further; nothing should change. + scroller.ComputeScrollOffset(start_time + scroll_duration * 2); + EXPECT_EQ(scroller.GetFinalX(), scroller.GetCurrX()); + EXPECT_EQ(scroller.GetFinalY(), scroller.GetCurrY()); + EXPECT_TRUE(scroller.IsFinished()); + EXPECT_EQ(scroll_duration, scroller.GetTimePassed()); +} + +TEST_F(ScrollerTest, Fling) { + Scroller scroller(DefaultConfig()); + base::TimeTicks start_time = base::TimeTicks::Now(); + + // Start a fling and verify initialized values. + scroller.Fling(kDefaultStartX, + kDefaultStartY, + kDefaultVelocityX, + kDefaultVelocityY, + INT_MIN, + INT_MAX, + INT_MIN, + INT_MAX, + start_time); + + EXPECT_EQ(kDefaultStartX, scroller.GetStartX()); + EXPECT_EQ(kDefaultStartY, scroller.GetStartY()); + EXPECT_EQ(kDefaultStartX, scroller.GetCurrX()); + EXPECT_EQ(kDefaultStartY, scroller.GetCurrY()); + EXPECT_GT(kDefaultStartX, scroller.GetFinalX()); + EXPECT_LT(kDefaultStartY, scroller.GetFinalY()); + EXPECT_FALSE(scroller.IsFinished()); + EXPECT_EQ(base::TimeDelta(), scroller.GetTimePassed()); + + // Advance halfway through the fling. + const base::TimeDelta scroll_duration = scroller.GetDuration(); + scroller.ComputeScrollOffset(start_time + scroll_duration / 2); + + // Ensure we've moved in the direction of the velocity, but have yet to reach + // the target. + EXPECT_GT(kDefaultStartX, scroller.GetCurrX()); + EXPECT_LT(kDefaultStartY, scroller.GetCurrY()); + EXPECT_LT(scroller.GetFinalX(), scroller.GetCurrX()); + EXPECT_GT(scroller.GetFinalY(), scroller.GetCurrY()); + EXPECT_FALSE(scroller.IsFinished()); + + // Ensure our velocity is non-zero and in the same direction as the original + // velocity. + EXPECT_LT(0.f, scroller.GetCurrVelocityX() * kDefaultVelocityX); + EXPECT_LT(0.f, scroller.GetCurrVelocityY() * kDefaultVelocityY); + EXPECT_TRUE( + scroller.IsScrollingInDirection(kDefaultVelocityX, kDefaultVelocityY)); + + // Repeated offset computations at the same timestamp should yield identical + // results. + float curr_x = scroller.GetCurrX(); + float curr_y = scroller.GetCurrY(); + float curr_velocity_x = scroller.GetCurrVelocityX(); + float curr_velocity_y = scroller.GetCurrVelocityY(); + scroller.ComputeScrollOffset(start_time + scroll_duration / 2); + EXPECT_EQ(curr_x, scroller.GetCurrX()); + EXPECT_EQ(curr_y, scroller.GetCurrY()); + EXPECT_EQ(curr_velocity_x, scroller.GetCurrVelocityX()); + EXPECT_EQ(curr_velocity_y, scroller.GetCurrVelocityY()); + + // Advance to the end. + scroller.ComputeScrollOffset(start_time + scroll_duration); + EXPECT_EQ(scroller.GetFinalX(), scroller.GetCurrX()); + EXPECT_EQ(scroller.GetFinalY(), scroller.GetCurrY()); + EXPECT_TRUE(scroller.IsFinished()); + EXPECT_EQ(scroll_duration, scroller.GetTimePassed()); + EXPECT_NEAR(0.f, scroller.GetCurrVelocityX(), kEpsilon); + EXPECT_NEAR(0.f, scroller.GetCurrVelocityY(), kEpsilon); + + // Try to advance further; nothing should change. + scroller.ComputeScrollOffset(start_time + scroll_duration * 2); + EXPECT_EQ(scroller.GetFinalX(), scroller.GetCurrX()); + EXPECT_EQ(scroller.GetFinalY(), scroller.GetCurrY()); + EXPECT_TRUE(scroller.IsFinished()); + EXPECT_EQ(scroll_duration, scroller.GetTimePassed()); +} + +} // namespace gfx diff --git a/chromium/ui/gfx/android/shared_device_display_info.cc b/chromium/ui/gfx/android/shared_device_display_info.cc index 9ac18d0218d..c58813dfcef 100644 --- a/chromium/ui/gfx/android/shared_device_display_info.cc +++ b/chromium/ui/gfx/android/shared_device_display_info.cc @@ -16,13 +16,18 @@ static void UpdateSharedDeviceDisplayInfo(JNIEnv* env, jobject obj, jint display_height, jint display_width, + jint physical_display_height, + jint physical_display_width, jint bits_per_pixel, jint bits_per_component, jdouble dip_scale, - jint smallest_dip_width) { + jint smallest_dip_width, + jint rotation_degrees) { SharedDeviceDisplayInfo::GetInstance()->InvokeUpdate(env, obj, - display_height, display_width, bits_per_pixel, bits_per_component, - dip_scale, smallest_dip_width); + display_height, display_width, + physical_display_height, physical_display_width, + bits_per_pixel, bits_per_component, + dip_scale, smallest_dip_width, rotation_degrees); } // static @@ -42,6 +47,16 @@ int SharedDeviceDisplayInfo::GetDisplayWidth() { return display_width_; } +int SharedDeviceDisplayInfo::GetPhysicalDisplayHeight() { + base::AutoLock autolock(lock_); + return physical_display_height_; +} + +int SharedDeviceDisplayInfo::GetPhysicalDisplayWidth() { + base::AutoLock autolock(lock_); + return physical_display_width_; +} + int SharedDeviceDisplayInfo::GetBitsPerPixel() { base::AutoLock autolock(lock_); DCHECK_NE(0, bits_per_pixel_); @@ -66,6 +81,11 @@ int SharedDeviceDisplayInfo::GetSmallestDIPWidth() { return smallest_dip_width_; } +int SharedDeviceDisplayInfo::GetRotationDegrees() { + base::AutoLock autolock(lock_); + return rotation_degrees_; +} + // static bool SharedDeviceDisplayInfo::RegisterSharedDeviceDisplayInfo(JNIEnv* env) { return RegisterNativesImpl(env); @@ -75,15 +95,20 @@ void SharedDeviceDisplayInfo::InvokeUpdate(JNIEnv* env, jobject obj, jint display_height, jint display_width, + jint physical_display_height, + jint physical_display_width, jint bits_per_pixel, jint bits_per_component, jdouble dip_scale, - jint smallest_dip_width) { + jint smallest_dip_width, + jint rotation_degrees) { base::AutoLock autolock(lock_); - UpdateDisplayInfo(env, obj, display_height, - display_width, bits_per_pixel, bits_per_component, dip_scale, - smallest_dip_width); + UpdateDisplayInfo(env, obj, + display_height, display_width, + physical_display_height, physical_display_width, + bits_per_pixel, bits_per_component, dip_scale, + smallest_dip_width, rotation_degrees); } SharedDeviceDisplayInfo::SharedDeviceDisplayInfo() @@ -95,15 +120,19 @@ SharedDeviceDisplayInfo::SharedDeviceDisplayInfo() smallest_dip_width_(0) { JNIEnv* env = base::android::AttachCurrentThread(); j_device_info_.Reset( - Java_DeviceDisplayInfo_createWithListener(env, - base::android::GetApplicationContext())); + Java_DeviceDisplayInfo_create( + env, base::android::GetApplicationContext())); UpdateDisplayInfo(env, j_device_info_.obj(), Java_DeviceDisplayInfo_getDisplayHeight(env, j_device_info_.obj()), Java_DeviceDisplayInfo_getDisplayWidth(env, j_device_info_.obj()), + Java_DeviceDisplayInfo_getPhysicalDisplayHeight(env, + j_device_info_.obj()), + Java_DeviceDisplayInfo_getPhysicalDisplayWidth(env, j_device_info_.obj()), Java_DeviceDisplayInfo_getBitsPerPixel(env, j_device_info_.obj()), Java_DeviceDisplayInfo_getBitsPerComponent(env, j_device_info_.obj()), Java_DeviceDisplayInfo_getDIPScale(env, j_device_info_.obj()), - Java_DeviceDisplayInfo_getSmallestDIPWidth(env, j_device_info_.obj())); + Java_DeviceDisplayInfo_getSmallestDIPWidth(env, j_device_info_.obj()), + Java_DeviceDisplayInfo_getRotationDegrees(env, j_device_info_.obj())); } SharedDeviceDisplayInfo::~SharedDeviceDisplayInfo() { @@ -113,16 +142,22 @@ void SharedDeviceDisplayInfo::UpdateDisplayInfo(JNIEnv* env, jobject jobj, jint display_height, jint display_width, + jint physical_display_height, + jint physical_display_width, jint bits_per_pixel, jint bits_per_component, jdouble dip_scale, - jint smallest_dip_width) { + jint smallest_dip_width, + jint rotation_degrees) { display_height_ = static_cast<int>(display_height); display_width_ = static_cast<int>(display_width); + physical_display_height_ = static_cast<int>(physical_display_height); + physical_display_width_ = static_cast<int>(physical_display_width); bits_per_pixel_ = static_cast<int>(bits_per_pixel); bits_per_component_ = static_cast<int>(bits_per_component); dip_scale_ = static_cast<double>(dip_scale); smallest_dip_width_ = static_cast<int>(smallest_dip_width); + rotation_degrees_ = static_cast<int>(rotation_degrees); } } // namespace gfx diff --git a/chromium/ui/gfx/android/shared_device_display_info.h b/chromium/ui/gfx/android/shared_device_display_info.h index 1e1fc0c6662..e6983e58aa3 100644 --- a/chromium/ui/gfx/android/shared_device_display_info.h +++ b/chromium/ui/gfx/android/shared_device_display_info.h @@ -18,12 +18,16 @@ class SharedDeviceDisplayInfo { public: static SharedDeviceDisplayInfo* GetInstance(); + // See documentation in DeviceDisplayInfo.java int GetDisplayHeight(); int GetDisplayWidth(); + int GetPhysicalDisplayHeight(); + int GetPhysicalDisplayWidth(); int GetBitsPerPixel(); int GetBitsPerComponent(); double GetDIPScale(); int GetSmallestDIPWidth(); + int GetRotationDegrees(); // Registers methods with JNI and returns true if succeeded. static bool RegisterSharedDeviceDisplayInfo(JNIEnv* env); @@ -32,10 +36,13 @@ class SharedDeviceDisplayInfo { jobject jobj, jint display_height, jint display_width, + jint physical_display_height, + jint physical_display_width, jint bits_per_pixel, jint bits_per_component, jdouble dip_scale, - jint smallest_dip_width); + jint smallest_dip_width, + jint rotation_degrees); private: friend struct DefaultSingletonTraits<SharedDeviceDisplayInfo>; @@ -45,20 +52,26 @@ class SharedDeviceDisplayInfo { jobject jobj, jint display_height, jint display_width, + jint physical_display_height, + jint physical_display_width, jint bits_per_pixel, jint bits_per_component, jdouble dip_scale, - jint smallest_dip_width); + jint smallest_dip_width, + jint rotation_degrees); base::Lock lock_; base::android::ScopedJavaGlobalRef<jobject> j_device_info_; int display_height_; int display_width_; + int physical_display_height_; + int physical_display_width_; int bits_per_pixel_; int bits_per_component_; double dip_scale_; int smallest_dip_width_; + int rotation_degrees_; DISALLOW_COPY_AND_ASSIGN(SharedDeviceDisplayInfo); }; diff --git a/chromium/ui/gfx/android/view_configuration.cc b/chromium/ui/gfx/android/view_configuration.cc index 63657178b15..16c8617ed6b 100644 --- a/chromium/ui/gfx/android/view_configuration.cc +++ b/chromium/ui/gfx/android/view_configuration.cc @@ -1,52 +1,206 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. +// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ui/gfx/android/view_configuration.h" #include "base/android/jni_android.h" -#include "jni/ViewConfiguration_jni.h" +#include "base/lazy_instance.h" +#include "base/threading/non_thread_safe.h" +#include "jni/ViewConfigurationHelper_jni.h" -using namespace JNI_ViewConfiguration; using base::android::AttachCurrentThread; using base::android::GetApplicationContext; namespace gfx { +namespace { + +struct ViewConfigurationData { + ViewConfigurationData() + : double_tap_timeout_in_ms_(0), + long_press_timeout_in_ms_(0), + tap_timeout_in_ms_(0), + scroll_friction_(1.f), + max_fling_velocity_in_pixels_s_(0), + min_fling_velocity_in_pixels_s_(0), + touch_slop_in_pixels_(0), + double_tap_slop_in_pixels_(0), + min_scaling_span_in_pixels_(0), + min_scaling_touch_major_in_pixels_(0) { + JNIEnv* env = AttachCurrentThread(); + j_view_configuration_helper_.Reset( + Java_ViewConfigurationHelper_createWithListener( + env, base::android::GetApplicationContext())); + + double_tap_timeout_in_ms_ = + Java_ViewConfigurationHelper_getDoubleTapTimeout(env); + long_press_timeout_in_ms_ = + Java_ViewConfigurationHelper_getLongPressTimeout(env); + tap_timeout_in_ms_ = Java_ViewConfigurationHelper_getTapTimeout(env); + scroll_friction_ = Java_ViewConfigurationHelper_getScrollFriction(env); + + jobject obj = j_view_configuration_helper_.obj(); + Update( + Java_ViewConfigurationHelper_getScaledMaximumFlingVelocity(env, obj), + Java_ViewConfigurationHelper_getScaledMinimumFlingVelocity(env, obj), + Java_ViewConfigurationHelper_getScaledTouchSlop(env, obj), + Java_ViewConfigurationHelper_getScaledDoubleTapSlop(env, obj), + Java_ViewConfigurationHelper_getScaledMinScalingSpan(env, obj), + Java_ViewConfigurationHelper_getScaledMinScalingTouchMajor(env, obj)); + } + + ~ViewConfigurationData() {} + + void SynchronizedUpdate(int scaled_maximum_fling_velocity, + int scaled_minimum_fling_velocity, + int scaled_touch_slop, + int scaled_double_tap_slop, + int scaled_min_scaling_span, + int scaled_min_scaling_touch_major) { + base::AutoLock autolock(lock_); + Update(scaled_maximum_fling_velocity, + scaled_minimum_fling_velocity, + scaled_touch_slop, + scaled_double_tap_slop, + scaled_min_scaling_span, + scaled_min_scaling_touch_major); + } + + int double_tap_timeout_in_ms() const { return double_tap_timeout_in_ms_; } + int long_press_timeout_in_ms() const { return long_press_timeout_in_ms_; } + int tap_timeout_in_ms() const { return tap_timeout_in_ms_; } + float scroll_friction() const { return scroll_friction_; } + + int max_fling_velocity_in_pixels_s() { + base::AutoLock autolock(lock_); + return max_fling_velocity_in_pixels_s_; + } + + int min_fling_velocity_in_pixels_s() { + base::AutoLock autolock(lock_); + return min_fling_velocity_in_pixels_s_; + } + + int touch_slop_in_pixels() { + base::AutoLock autolock(lock_); + return touch_slop_in_pixels_; + } + + int double_tap_slop_in_pixels() { + base::AutoLock autolock(lock_); + return double_tap_slop_in_pixels_; + } + + int min_scaling_span_in_pixels() { + base::AutoLock autolock(lock_); + return min_scaling_span_in_pixels_; + } + + int min_scaling_touch_major_in_pixels() { + base::AutoLock autolock(lock_); + return min_scaling_touch_major_in_pixels_; + } + + private: + void Update(int scaled_maximum_fling_velocity, + int scaled_minimum_fling_velocity, + int scaled_touch_slop, + int scaled_double_tap_slop, + int scaled_min_scaling_span, + int scaled_min_scaling_touch_major) { + DCHECK_LE(scaled_minimum_fling_velocity, scaled_maximum_fling_velocity); + max_fling_velocity_in_pixels_s_ = scaled_maximum_fling_velocity; + min_fling_velocity_in_pixels_s_ = scaled_minimum_fling_velocity; + touch_slop_in_pixels_ = scaled_touch_slop; + double_tap_slop_in_pixels_ = scaled_double_tap_slop; + min_scaling_span_in_pixels_ = scaled_min_scaling_span; + min_scaling_touch_major_in_pixels_ = scaled_min_scaling_touch_major; + } + + base::Lock lock_; + base::android::ScopedJavaGlobalRef<jobject> j_view_configuration_helper_; + + // These values will remain constant throughout the lifetime of the app, so + // read-access needn't be synchronized. + int double_tap_timeout_in_ms_; + int long_press_timeout_in_ms_; + int tap_timeout_in_ms_; + float scroll_friction_; + + // These values may vary as view-specific parameters (DPI scale) are changed, + // so read/write access must be synchronized. + int max_fling_velocity_in_pixels_s_; + int min_fling_velocity_in_pixels_s_; + int touch_slop_in_pixels_; + int double_tap_slop_in_pixels_; + int min_scaling_span_in_pixels_; + int min_scaling_touch_major_in_pixels_; + + private: + DISALLOW_COPY_AND_ASSIGN(ViewConfigurationData); +}; + +// Leaky to allow access from any thread. +base::LazyInstance<ViewConfigurationData>::Leaky g_view_configuration = + LAZY_INSTANCE_INITIALIZER; + +} // namespace + +static void UpdateSharedViewConfiguration(JNIEnv* env, + jobject obj, + jint scaled_maximum_fling_velocity, + jint scaled_minimum_fling_velocity, + jint scaled_touch_slop, + jint scaled_double_tap_slop, + jint scaled_min_scaling_span, + jint scaled_min_scaling_touch_major) { + g_view_configuration.Get().SynchronizedUpdate(scaled_maximum_fling_velocity, + scaled_minimum_fling_velocity, + scaled_touch_slop, + scaled_double_tap_slop, + scaled_min_scaling_span, + scaled_min_scaling_touch_major); +} + int ViewConfiguration::GetDoubleTapTimeoutInMs() { - JNIEnv* env = AttachCurrentThread(); - return Java_ViewConfiguration_getDoubleTapTimeout(env); + return g_view_configuration.Get().double_tap_timeout_in_ms(); } int ViewConfiguration::GetLongPressTimeoutInMs() { - JNIEnv* env = AttachCurrentThread(); - return Java_ViewConfiguration_getLongPressTimeout(env); + return g_view_configuration.Get().long_press_timeout_in_ms(); } int ViewConfiguration::GetTapTimeoutInMs() { - JNIEnv* env = AttachCurrentThread(); - return Java_ViewConfiguration_getTapTimeout(env); + return g_view_configuration.Get().tap_timeout_in_ms(); +} + +float ViewConfiguration::GetScrollFriction() { + return g_view_configuration.Get().scroll_friction(); } int ViewConfiguration::GetMaximumFlingVelocityInPixelsPerSecond() { - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef<jobject> view = - Java_ViewConfiguration_get(env, GetApplicationContext()); - return Java_ViewConfiguration_getScaledMaximumFlingVelocity(env, view.obj()); + return g_view_configuration.Get().max_fling_velocity_in_pixels_s(); } int ViewConfiguration::GetMinimumFlingVelocityInPixelsPerSecond() { - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef<jobject> view = - Java_ViewConfiguration_get(env, GetApplicationContext()); - return Java_ViewConfiguration_getScaledMinimumFlingVelocity(env, view.obj()); + return g_view_configuration.Get().min_fling_velocity_in_pixels_s(); } int ViewConfiguration::GetTouchSlopInPixels() { - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef<jobject> view = - Java_ViewConfiguration_get(env, GetApplicationContext()); - return Java_ViewConfiguration_getScaledTouchSlop(env, view.obj()); + return g_view_configuration.Get().touch_slop_in_pixels(); +} + +int ViewConfiguration::GetDoubleTapSlopInPixels() { + return g_view_configuration.Get().double_tap_slop_in_pixels(); +} + +int ViewConfiguration::GetMinScalingSpanInPixels() { + return g_view_configuration.Get().min_scaling_span_in_pixels(); +} + +int ViewConfiguration::GetMinScalingTouchMajorInPixels() { + return g_view_configuration.Get().min_scaling_touch_major_in_pixels(); } bool ViewConfiguration::RegisterViewConfiguration(JNIEnv* env) { diff --git a/chromium/ui/gfx/android/view_configuration.h b/chromium/ui/gfx/android/view_configuration.h index f0995cb3e29..4595071c4f1 100644 --- a/chromium/ui/gfx/android/view_configuration.h +++ b/chromium/ui/gfx/android/view_configuration.h @@ -12,16 +12,24 @@ namespace gfx { // Provides access to Android's ViewConfiguration for gesture-related constants. +// Note: All methods may be safely called from any thread. class GFX_EXPORT ViewConfiguration { public: static int GetDoubleTapTimeoutInMs(); static int GetLongPressTimeoutInMs(); static int GetTapTimeoutInMs(); + // Dimensionless coefficient of friction. + static float GetScrollFriction(); + static int GetMaximumFlingVelocityInPixelsPerSecond(); static int GetMinimumFlingVelocityInPixelsPerSecond(); static int GetTouchSlopInPixels(); + static int GetDoubleTapSlopInPixels(); + + static int GetMinScalingSpanInPixels(); + static int GetMinScalingTouchMajorInPixels(); // Registers methods with JNI and returns true if succeeded. static bool RegisterViewConfiguration(JNIEnv* env); diff --git a/chromium/ui/gfx/animation/animation.cc b/chromium/ui/gfx/animation/animation.cc index 3b525f22f00..fce72e8fce5 100644 --- a/chromium/ui/gfx/animation/animation.cc +++ b/chromium/ui/gfx/animation/animation.cc @@ -106,8 +106,9 @@ bool Animation::ShouldRenderRichAnimation() { } } return !::GetSystemMetrics(SM_REMOTESESSION); -#endif +#else return true; +#endif } bool Animation::ShouldSendCanceledFromStop() { diff --git a/chromium/ui/gfx/animation/animation_delegate.h b/chromium/ui/gfx/animation/animation_delegate.h index 94303ddd93a..b1e88d2fdac 100644 --- a/chromium/ui/gfx/animation/animation_delegate.h +++ b/chromium/ui/gfx/animation/animation_delegate.h @@ -17,6 +17,8 @@ class Animation; // state of an animation. class GFX_EXPORT AnimationDelegate { public: + virtual ~AnimationDelegate() {} + // Called when an animation has completed. virtual void AnimationEnded(const Animation* animation) {} @@ -25,9 +27,6 @@ class GFX_EXPORT AnimationDelegate { // Called when an animation has been canceled. virtual void AnimationCanceled(const Animation* animation) {} - - protected: - virtual ~AnimationDelegate() {} }; } // namespace gfx diff --git a/chromium/ui/gfx/animation/tween.cc b/chromium/ui/gfx/animation/tween.cc index 174305078c2..483d772b5b4 100644 --- a/chromium/ui/gfx/animation/tween.cc +++ b/chromium/ui/gfx/animation/tween.cc @@ -14,6 +14,7 @@ #include "base/basictypes.h" #include "base/logging.h" +#include "ui/gfx/geometry/cubic_bezier.h" #include "ui/gfx/safe_integer_conversions.h" namespace gfx { @@ -51,6 +52,15 @@ double Tween::CalculateValue(Tween::Type type, double state) { case SMOOTH_IN_OUT: return sin(state); + case FAST_OUT_SLOW_IN: + return gfx::CubicBezier(0.4, 0, 0.2, 1).Solve(state); + + case LINEAR_OUT_SLOW_IN: + return gfx::CubicBezier(0, 0, .2, 1).Solve(state); + + case FAST_OUT_LINEAR_IN: + return gfx::CubicBezier(0.4, 0, 1, 1).Solve(state); + case ZERO: return 0; } diff --git a/chromium/ui/gfx/animation/tween.h b/chromium/ui/gfx/animation/tween.h index 04f353db1ae..8b079ac73cb 100644 --- a/chromium/ui/gfx/animation/tween.h +++ b/chromium/ui/gfx/animation/tween.h @@ -16,15 +16,22 @@ namespace gfx { class GFX_EXPORT Tween { public: enum Type { - LINEAR, // Linear. - EASE_OUT, // Fast in, slow out (default). - EASE_IN, // Slow in, fast out. - EASE_IN_2, // Variant of EASE_IN that starts out slower. - EASE_IN_OUT, // Slow in and out, fast in the middle. - FAST_IN_OUT, // Fast in and out, slow in the middle. - EASE_OUT_SNAP, // Fast in, slow out, snap to final value. - SMOOTH_IN_OUT, // Smooth, consistent speeds in and out (sine wave). - ZERO, // Returns a value of 0 always. + LINEAR, // Linear. + EASE_OUT, // Fast in, slow out (default). + EASE_IN, // Slow in, fast out. + EASE_IN_2, // Variant of EASE_IN that starts out slower than + // EASE_IN. + EASE_IN_OUT, // Slow in and out, fast in the middle. + FAST_IN_OUT, // Fast in and out, slow in the middle. + EASE_OUT_SNAP, // Fast in, slow out, snap to final value. + SMOOTH_IN_OUT, // Smooth, consistent speeds in and out (sine wave). + FAST_OUT_SLOW_IN, // Variant of EASE_IN_OUT which should be used in most + // cases. + LINEAR_OUT_SLOW_IN, // Variant of EASE_OUT which should be used for + // fading in from 0% or motion when entering a scene. + FAST_OUT_LINEAR_IN, // Variant of EASE_IN which should should be used for + // fading out to 0% or motion when exiting a scene. + ZERO, // Returns a value of 0 always. }; // Returns the value based on the tween type. |state| is from 0-1. diff --git a/chromium/ui/gfx/animation/tween_unittest.cc b/chromium/ui/gfx/animation/tween_unittest.cc index d92fe1bacc4..f71a77dccbd 100644 --- a/chromium/ui/gfx/animation/tween_unittest.cc +++ b/chromium/ui/gfx/animation/tween_unittest.cc @@ -11,16 +11,19 @@ #endif #include "testing/gtest/include/gtest/gtest.h" -#include "ui/gfx/test/color_util.h" +#include "ui/gfx/test/gfx_util.h" namespace gfx { namespace { double next_double(double d) { #if defined(OS_WIN) - return _nextafter(d, d+1); + return _nextafter(d, d + 1); #else - return nextafter(d, d+1); + // Step two units of least precision towards positive infinity. On some 32 + // bit x86 compilers a single step was not enough due to loss of precision in + // optimized code. + return nextafter(nextafter(d, d + 1), d + 1); #endif } diff --git a/chromium/ui/gfx/blit.cc b/chromium/ui/gfx/blit.cc index 4b87bc9aac5..653c56bda7f 100644 --- a/chromium/ui/gfx/blit.cc +++ b/chromium/ui/gfx/blit.cc @@ -29,21 +29,28 @@ namespace { // Returns true if the given canvas has any part of itself clipped out or // any non-identity tranform. -bool HasClipOrTransform(const SkCanvas& canvas) { +bool HasClipOrTransform(SkCanvas& canvas) { if (!canvas.getTotalMatrix().isIdentity()) return true; - const SkRegion& clip_region = canvas.getTotalClip(); - if (clip_region.isEmpty() || clip_region.isComplex()) + if (!canvas.isClipRect()) return true; // Now we know the clip is a regular rectangle, make sure it covers the // entire canvas. - const SkBitmap& bitmap = skia::GetTopDevice(canvas)->accessBitmap(false); - const SkIRect& clip_bounds = clip_region.getBounds(); + SkIRect clip_bounds; + canvas.getClipDeviceBounds(&clip_bounds); + + SkImageInfo info; + size_t row_bytes; + void* pixels = canvas.accessTopLayerPixels(&info, &row_bytes); + DCHECK(pixels); + if (!pixels) + return true; + if (clip_bounds.fLeft != 0 || clip_bounds.fTop != 0 || - clip_bounds.fRight != bitmap.width() || - clip_bounds.fBottom != bitmap.height()) + clip_bounds.fRight != info.width() || + clip_bounds.fBottom != info.height()) return true; return false; diff --git a/chromium/ui/gfx/box_f.h b/chromium/ui/gfx/box_f.h index 73e0972f662..47d822eb33f 100644 --- a/chromium/ui/gfx/box_f.h +++ b/chromium/ui/gfx/box_f.h @@ -2,159 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_GFX_BOX_F_H_ -#define UI_GFX_BOX_F_H_ +// TODO(beng): remove once callsites are patched. +#include "ui/gfx/geometry/box_f.h" -#include "ui/gfx/point3_f.h" -#include "ui/gfx/vector3d_f.h" - -namespace gfx { - -// A 3d version of gfx::RectF, with the positive z-axis pointed towards -// the camera. -class GFX_EXPORT BoxF { - public: - BoxF() - : width_(0.f), - height_(0.f), - depth_(0.f) {} - - BoxF(float width, float height, float depth) - : width_(width < 0 ? 0 : width), - height_(height < 0 ? 0 : height), - depth_(depth < 0 ? 0 : depth) {} - - BoxF(float x, float y, float z, float width, float height, float depth) - : origin_(x, y, z), - width_(width < 0 ? 0 : width), - height_(height < 0 ? 0 : height), - depth_(depth < 0 ? 0 : depth) {} - - BoxF(const Point3F& origin, float width, float height, float depth) - : origin_(origin), - width_(width < 0 ? 0 : width), - height_(height < 0 ? 0 : height), - depth_(depth < 0 ? 0 : depth) {} - - ~BoxF() {} - - // Scales all three axes by the given scale. - void Scale(float scale) { - Scale(scale, scale, scale); - } - - // Scales each axis by the corresponding given scale. - void Scale(float x_scale, float y_scale, float z_scale) { - origin_.Scale(x_scale, y_scale, z_scale); - set_size(width_ * x_scale, height_ * y_scale, depth_ * z_scale); - } - - // Moves the box by the specified distance in each dimension. - void operator+=(const Vector3dF& offset) { - origin_ += offset; - } - - // Returns true if the box has no interior points. - bool IsEmpty() const; - - // Computes the union of this box with the given box. The union is the - // smallest box that contains both boxes. - void Union(const BoxF& box); - - std::string ToString() const; - - float x() const { return origin_.x(); } - void set_x(float x) { origin_.set_x(x); } - - float y() const { return origin_.y(); } - void set_y(float y) { origin_.set_y(y); } - - float z() const { return origin_.z(); } - void set_z(float z) { origin_.set_z(z); } - - float width() const { return width_; } - void set_width(float width) { width_ = width < 0 ? 0 : width; } - - float height() const { return height_; } - void set_height(float height) { height_ = height < 0 ? 0 : height; } - - float depth() const { return depth_; } - void set_depth(float depth) { depth_ = depth < 0 ? 0 : depth; } - - float right() const { return x() + width(); } - float bottom() const { return y() + height(); } - float front() const { return z() + depth(); } - - void set_size(float width, float height, float depth) { - width_ = width < 0 ? 0 : width; - height_ = height < 0 ? 0 : height; - depth_ = depth < 0 ? 0 : depth; - } - - const Point3F& origin() const { return origin_; } - void set_origin(const Point3F& origin) { origin_ = origin; } - - // Expands |this| to contain the given point, if necessary. Please note, even - // if |this| is empty, after the function |this| will continue to contain its - // |origin_|. - void ExpandTo(const Point3F& point); - - // Expands |this| to contain the given box, if necessary. Please note, even - // if |this| is empty, after the function |this| will continue to contain its - // |origin_|. - void ExpandTo(const BoxF& box); - - private: - // Expands the box to contain the two given points. It is required that each - // component of |min| is less than or equal to the corresponding component in - // |max|. Precisely, what this function does is ensure that after the function - // completes, |this| contains origin_, min, max, and origin_ + (width_, - // height_, depth_), even if the box is empty. Emptiness checks are handled in - // the public function Union. - void ExpandTo(const Point3F& min, const Point3F& max); - - Point3F origin_; - float width_; - float height_; - float depth_; -}; - -GFX_EXPORT BoxF UnionBoxes(const BoxF& a, const BoxF& b); - -inline BoxF ScaleBox(const BoxF& b, - float x_scale, - float y_scale, - float z_scale) { - return BoxF(b.x() * x_scale, - b.y() * y_scale, - b.z() * z_scale, - b.width() * x_scale, - b.height() * y_scale, - b.depth() * z_scale); -} - -inline BoxF ScaleBox(const BoxF& b, float scale) { - return ScaleBox(b, scale, scale, scale); -} - -inline bool operator==(const BoxF& a, const BoxF& b) { - return a.origin() == b.origin() && a.width() == b.width() && - a.height() == b.height() && a.depth() == b.depth(); -} - -inline bool operator!=(const BoxF& a, const BoxF& b) { - return !(a == b); -} - -inline BoxF operator+(const BoxF& b, const Vector3dF& v) { - return BoxF(b.x() + v.x(), - b.y() + v.y(), - b.z() + v.z(), - b.width(), - b.height(), - b.depth()); -} - -} // namespace gfx - -#endif // UI_GFX_BOX_F_H_ diff --git a/chromium/ui/gfx/canvas.cc b/chromium/ui/gfx/canvas.cc index b89efa9e2b7..db8b28fc0a9 100644 --- a/chromium/ui/gfx/canvas.cc +++ b/chromium/ui/gfx/canvas.cc @@ -11,7 +11,6 @@ #include "base/logging.h" #include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/effects/SkGradientShader.h" -#include "ui/gfx/canvas.h" #include "ui/gfx/font_list.h" #include "ui/gfx/rect.h" #include "ui/gfx/size_conversions.h" @@ -99,16 +98,6 @@ void Canvas::SizeStringInt(const base::string16& text, } // static -void Canvas::SizeStringInt(const base::string16& text, - const Font& font, - int* width, - int* height, - int line_height, - int flags) { - SizeStringInt(text, FontList(font), width, height, line_height, flags); -} - -// static int Canvas::GetStringWidth(const base::string16& text, const FontList& font_list) { int width = 0, height = 0; @@ -125,39 +114,19 @@ float Canvas::GetStringWidthF(const base::string16& text, } // static -int Canvas::GetStringWidth(const base::string16& text, const Font& font) { - int width = 0, height = 0; - SizeStringInt(text, FontList(font), &width, &height, 0, NO_ELLIPSIS); - return width; -} - -// static int Canvas::DefaultCanvasTextAlignment() { return base::i18n::IsRTL() ? TEXT_ALIGN_RIGHT : TEXT_ALIGN_LEFT; } -void Canvas::DrawStringWithHalo(const base::string16& text, - const Font& font, - SkColor text_color, - SkColor halo_color_in, - int x, - int y, - int w, - int h, - int flags) { - DrawStringRectWithHalo(text, FontList(font), text_color, halo_color_in, - Rect(x, y, w, h), flags); -} - ImageSkiaRep Canvas::ExtractImageRep() const { - const SkBitmap& device_bitmap = canvas_->getDevice()->accessBitmap(false); - // Make a bitmap to return, and a canvas to draw into it. We don't just want // to call extractSubset or the copy constructor, since we want an actual copy // of the bitmap. + const SkISize size = canvas_->getDeviceSize(); SkBitmap result; - device_bitmap.copyTo(&result, SkBitmap::kARGB_8888_Config); + result.allocN32Pixels(size.width(), size.height()); + canvas_->readPixels(&result, 0, 0); return ImageSkiaRep(result, image_scale_); } @@ -175,8 +144,7 @@ void Canvas::DrawDashedRect(const Rect& rect, SkColor color) { delete dots; last_color = color; dots = new SkBitmap; - dots->setConfig(SkBitmap::kARGB_8888_Config, col_pixels, row_pixels); - dots->allocPixels(); + dots->allocN32Pixels(col_pixels, row_pixels); dots->eraseARGB(0, 0, 0, 0); uint32_t* dot = dots->getAddr32(0, 0); @@ -216,7 +184,6 @@ void Canvas::SaveLayerAlpha(uint8 alpha) { canvas_->saveLayerAlpha(NULL, alpha); } - void Canvas::SaveLayerAlpha(uint8 alpha, const Rect& layer_bounds) { SkRect bounds(RectToSkRect(layer_bounds)); canvas_->saveLayerAlpha(&bounds, alpha); @@ -226,12 +193,16 @@ void Canvas::Restore() { canvas_->restore(); } -bool Canvas::ClipRect(const Rect& rect) { - return canvas_->clipRect(RectToSkRect(rect)); +void Canvas::ClipRect(const Rect& rect) { + canvas_->clipRect(RectToSkRect(rect)); +} + +void Canvas::ClipPath(const SkPath& path, bool do_anti_alias) { + canvas_->clipPath(path, SkRegion::kIntersect_Op, do_anti_alias); } -bool Canvas::ClipPath(const SkPath& path) { - return canvas_->clipPath(path); +bool Canvas::IsClipEmpty() const { + return canvas_->isClipEmpty(); } bool Canvas::GetClipBounds(Rect* bounds) { @@ -363,7 +334,7 @@ void Canvas::DrawImageInt(const ImageSkia& image, int x, int y, const SkPaint& paint) { - const ImageSkiaRep& image_rep = GetImageRepToPaint(image); + const ImageSkiaRep& image_rep = image.GetRepresentation(image_scale_); if (image_rep.is_null()) return; const SkBitmap& bitmap = image_rep.sk_bitmap(); @@ -405,63 +376,65 @@ void Canvas::DrawImageInt(const ImageSkia& image, int dest_h, bool filter, const SkPaint& paint) { - DLOG_ASSERT(src_x + src_w < std::numeric_limits<int16_t>::max() && - src_y + src_h < std::numeric_limits<int16_t>::max()); - if (src_w <= 0 || src_h <= 0) { - NOTREACHED() << "Attempting to draw bitmap from an empty rect!"; - return; - } - - if (!IntersectsClipRectInt(dest_x, dest_y, dest_w, dest_h)) - return; - - float user_scale_x = static_cast<float>(dest_w) / src_w; - float user_scale_y = static_cast<float>(dest_h) / src_h; - - const ImageSkiaRep& image_rep = GetImageRepToPaint(image, - user_scale_x, user_scale_y); - if (image_rep.is_null()) - return; - - SkRect dest_rect = { SkIntToScalar(dest_x), - SkIntToScalar(dest_y), - SkIntToScalar(dest_x + dest_w), - SkIntToScalar(dest_y + dest_h) }; - - if (src_w == dest_w && src_h == dest_h && - user_scale_x == 1.0f && user_scale_y == 1.0f && - image_rep.scale() == 1.0f) { - // Workaround for apparent bug in Skia that causes image to occasionally - // shift. - SkIRect src_rect = { src_x, src_y, src_x + src_w, src_y + src_h }; - const SkBitmap& bitmap = image_rep.sk_bitmap(); - canvas_->drawBitmapRect(bitmap, &src_rect, dest_rect, &paint); - return; - } - - // Make a bitmap shader that contains the bitmap we want to draw. This is - // basically what SkCanvas.drawBitmap does internally, but it gives us - // more control over quality and will use the mipmap in the source image if - // it has one, whereas drawBitmap won't. - SkMatrix shader_scale; - shader_scale.setScale(SkFloatToScalar(user_scale_x), - SkFloatToScalar(user_scale_y)); - shader_scale.preTranslate(SkIntToScalar(-src_x), SkIntToScalar(-src_y)); - shader_scale.postTranslate(SkIntToScalar(dest_x), SkIntToScalar(dest_y)); - - skia::RefPtr<SkShader> shader = CreateImageRepShader( - image_rep, - SkShader::kRepeat_TileMode, - shader_scale); - - // Set up our paint to use the shader & release our reference (now just owned - // by the paint). - SkPaint p(paint); - p.setFilterBitmap(filter); - p.setShader(shader.get()); - - // The rect will be filled by the bitmap. - canvas_->drawRect(dest_rect, p); + DrawImageIntHelper(image, src_x, src_y, src_w, src_h, dest_x, dest_y, dest_w, + dest_h, filter, paint, image_scale_, false); +} + +void Canvas::DrawImageIntInPixel(const ImageSkia& image, + int src_x, + int src_y, + int src_w, + int src_h, + int dest_x, + int dest_y, + int dest_w, + int dest_h, + bool filter, + const SkPaint& paint) { + // All values passed into this function are in pixels, i.e. no scaling needs + // be done. + // Logic as below:- + // 1. Get the matrix transform from the canvas. + // 2. Set the scale in the matrix to 1.0 while honoring the direction of the + // the scale (x/y). Example RTL layouts. + // 3. Round off the X and Y translation components in the matrix. This is to + // reduce floating point errors during rect transformation. This is needed + // for fractional scale factors like 1.25/1.5, etc. + // 4. Save the current state of the canvas. + // 5. Set the modified matrix in the canvas. This ensures that no scaling + // will be done for draw operations on the canvas. + // 6. Draw the image. + // 7. Restore the state of the canvas and the SkCanvas matrix stack. + SkMatrix matrix = canvas_->getTotalMatrix(); + + // Ensure that the direction of the x and y scales is preserved. This is + // important for RTL layouts. + matrix.getScaleX() > 0 ? matrix.setScaleX(1.0f) : matrix.setScaleX(-1.0f); + matrix.getScaleY() > 0 ? matrix.setScaleY(1.0f) : matrix.setScaleY(-1.0f); + + matrix.setTranslateX(SkScalarRoundToInt(matrix.getTranslateX())); + matrix.setTranslateY(SkScalarRoundToInt(matrix.getTranslateY())); + + Save(); + + canvas_->setMatrix(matrix); + + DrawImageIntHelper(image, + src_x, + src_y, + src_w, + src_h, + dest_x, + dest_y, + dest_w, + dest_h, + filter, + paint, + image_scale_, + true); + + // Restore the state of the canvas. + Restore(); } void Canvas::DrawImageInPath(const ImageSkia& image, @@ -469,7 +442,7 @@ void Canvas::DrawImageInPath(const ImageSkia& image, int y, const SkPath& path, const SkPaint& paint) { - const ImageSkiaRep& image_rep = GetImageRepToPaint(image); + const ImageSkiaRep& image_rep = image.GetRepresentation(image_scale_); if (image_rep.is_null()) return; @@ -502,47 +475,6 @@ void Canvas::DrawStringRectWithFlags(const base::string16& text, ShadowValues()); } -void Canvas::DrawStringInt(const base::string16& text, - const Font& font, - SkColor color, - int x, - int y, - int w, - int h) { - DrawStringInt(text, font, color, x, y, w, h, DefaultCanvasTextAlignment()); -} - -void Canvas::DrawStringInt(const base::string16& text, - const Font& font, - SkColor color, - const Rect& display_rect) { - DrawStringInt(text, font, color, display_rect.x(), display_rect.y(), - display_rect.width(), display_rect.height()); -} - -void Canvas::DrawStringInt(const base::string16& text, - const Font& font, - SkColor color, - int x, - int y, - int w, - int h, - int flags) { - DrawStringWithShadows(text, font, color, Rect(x, y, w, h), 0, flags, - ShadowValues()); -} - -void Canvas::DrawStringWithShadows(const base::string16& text, - const Font& font, - SkColor color, - const Rect& text_bounds, - int line_height, - int flags, - const ShadowValues& shadows) { - DrawStringRectWithShadows(text, FontList(font), color, text_bounds, - line_height, flags, shadows); -} - void Canvas::TileImageInt(const ImageSkia& image, int x, int y, @@ -573,8 +505,7 @@ void Canvas::TileImageInt(const ImageSkia& image, if (!IntersectsClipRectInt(dest_x, dest_y, w, h)) return; - const ImageSkiaRep& image_rep = GetImageRepToPaint( - image, tile_scale_x, tile_scale_y); + const ImageSkiaRep& image_rep = image.GetRepresentation(image_scale_); if (image_rep.is_null()) return; @@ -631,29 +562,77 @@ bool Canvas::IntersectsClipRect(const Rect& rect) { rect.width(), rect.height()); } -const ImageSkiaRep& Canvas::GetImageRepToPaint(const ImageSkia& image) const { - return GetImageRepToPaint(image, 1.0f, 1.0f); -} +void Canvas::DrawImageIntHelper(const ImageSkia& image, + int src_x, + int src_y, + int src_w, + int src_h, + int dest_x, + int dest_y, + int dest_w, + int dest_h, + bool filter, + const SkPaint& paint, + float image_scale, + bool pixel) { + DLOG_ASSERT(src_x + src_w < std::numeric_limits<int16_t>::max() && + src_y + src_h < std::numeric_limits<int16_t>::max()); + if (src_w <= 0 || src_h <= 0) { + NOTREACHED() << "Attempting to draw bitmap from an empty rect!"; + return; + } -const ImageSkiaRep& Canvas::GetImageRepToPaint( - const ImageSkia& image, - float user_additional_scale_x, - float user_additional_scale_y) const { - const ImageSkiaRep& image_rep = image.GetRepresentation(image_scale_); + if (!IntersectsClipRectInt(dest_x, dest_y, dest_w, dest_h)) + return; + + float user_scale_x = static_cast<float>(dest_w) / src_w; + float user_scale_y = static_cast<float>(dest_h) / src_h; + + const ImageSkiaRep& image_rep = image.GetRepresentation(image_scale); + if (image_rep.is_null()) + return; - if (!image_rep.is_null()) { - SkMatrix m = canvas_->getTotalMatrix(); - float scale_x = SkScalarToFloat(SkScalarAbs(m.getScaleX())) * - user_additional_scale_x; - float scale_y = SkScalarToFloat(SkScalarAbs(m.getScaleY())) * - user_additional_scale_y; + SkRect dest_rect = { SkIntToScalar(dest_x), + SkIntToScalar(dest_y), + SkIntToScalar(dest_x + dest_w), + SkIntToScalar(dest_y + dest_h) }; - float bitmap_scale = image_rep.scale(); - if (scale_x < bitmap_scale || scale_y < bitmap_scale) - const_cast<SkBitmap&>(image_rep.sk_bitmap()).buildMipMap(); + if (src_w == dest_w && src_h == dest_h && + user_scale_x == 1.0f && user_scale_y == 1.0f && + image_rep.scale() == 1.0f && !pixel) { + // Workaround for apparent bug in Skia that causes image to occasionally + // shift. + SkIRect src_rect = { src_x, src_y, src_x + src_w, src_y + src_h }; + const SkBitmap& bitmap = image_rep.sk_bitmap(); + canvas_->drawBitmapRect(bitmap, &src_rect, dest_rect, &paint); + return; } - return image_rep; + // Make a bitmap shader that contains the bitmap we want to draw. This is + // basically what SkCanvas.drawBitmap does internally, but it gives us + // more control over quality and will use the mipmap in the source image if + // it has one, whereas drawBitmap won't. + SkMatrix shader_scale; + shader_scale.setScale(SkFloatToScalar(user_scale_x), + SkFloatToScalar(user_scale_y)); + shader_scale.preTranslate(SkIntToScalar(-src_x), SkIntToScalar(-src_y)); + shader_scale.postTranslate(SkIntToScalar(dest_x), SkIntToScalar(dest_y)); + + skia::RefPtr<SkShader> shader = CreateImageRepShaderForScale( + image_rep, + SkShader::kRepeat_TileMode, + shader_scale, + pixel ? 1.0f : image_rep.scale()); + + // Set up our paint to use the shader & release our reference (now just owned + // by the paint). + SkPaint p(paint); + p.setFilterLevel(filter ? SkPaint::kLow_FilterLevel + : SkPaint::kNone_FilterLevel); + p.setShader(shader.get()); + + // The rect will be filled by the bitmap. + canvas_->drawRect(dest_rect, p); } } // namespace gfx diff --git a/chromium/ui/gfx/canvas.h b/chromium/ui/gfx/canvas.h index f8193f1134c..f3d9486f9a6 100644 --- a/chromium/ui/gfx/canvas.h +++ b/chromium/ui/gfx/canvas.h @@ -15,11 +15,11 @@ #include "ui/gfx/image/image_skia.h" #include "ui/gfx/native_widget_types.h" #include "ui/gfx/shadow_value.h" +#include "ui/gfx/text_constants.h" namespace gfx { class Rect; -class Font; class FontList; class Point; class Size; @@ -40,12 +40,7 @@ class Transform; // of kSrcOver_Mode. class GFX_EXPORT Canvas { public: - enum TruncateFadeMode { - TruncateFadeTail, - TruncateFadeHead, - }; - - // Specifies the alignment for text rendered with the DrawStringInt method. + // Specifies the alignment for text rendered with the DrawStringRect method. enum { TEXT_ALIGN_LEFT = 1 << 0, TEXT_ALIGN_CENTER = 1 << 1, @@ -54,7 +49,7 @@ class GFX_EXPORT Canvas { // Specifies the text consists of multiple lines. MULTI_LINE = 1 << 3, - // By default DrawStringInt does not process the prefix ('&') character + // By default DrawStringRect does not process the prefix ('&') character // specially. That is, the string "&foo" is rendered as "&foo". When // rendering text from a resource that uses the prefix character for // mnemonics, the prefix should be processed and can be rendered as an @@ -69,7 +64,7 @@ class GFX_EXPORT Canvas { // This only works with MULTI_LINE. CHARACTER_BREAK = 1 << 7, - // Instructs DrawStringInt() to render the text using RTL directionality. + // Instructs DrawStringRect() to render the text using RTL directionality. // In most cases, passing this flag is not necessary because information // about the text directionality is going to be embedded within the string // in the form of special Unicode characters. However, we don't insert @@ -83,7 +78,7 @@ class GFX_EXPORT Canvas { // See FORCE_RTL_DIRECTIONALITY for details. FORCE_LTR_DIRECTIONALITY = 1 << 9, - // Instructs DrawStringInt() to not use subpixel rendering. This is useful + // Instructs DrawStringRect() to not use subpixel rendering. This is useful // when rendering text onto a fully- or partially-transparent background // that will later be blended with another image. NO_SUBPIXEL_RENDERING = 1 << 10, @@ -130,13 +125,6 @@ class GFX_EXPORT Canvas { int* height, int line_height, int flags); - // Obsolete version. Use the above version which takes FontList. - static void SizeStringInt(const base::string16& text, - const Font& font, - int* width, - int* height, - int line_height, - int flags); // This is same as SizeStringInt except that fractional size is returned. // See comment in GetStringWidthF for its usage. @@ -151,8 +139,6 @@ class GFX_EXPORT Canvas { // |text| with |font_list|. static int GetStringWidth(const base::string16& text, const FontList& font_list); - // Obsolete version. Use the above version which takes FontList. - static int GetStringWidth(const base::string16& text, const Font& font); // This is same as GetStringWidth except that fractional width is returned. // Use this method for the scenario that multiple string widths need to be @@ -164,7 +150,7 @@ class GFX_EXPORT Canvas { // Returns the default text alignment to be used when drawing text on a // Canvas based on the directionality of the system locale language. - // This function is used by Canvas::DrawStringInt when the text alignment + // This function is used by Canvas::DrawStringRect when the text alignment // is not specified. // // This function returns either Canvas::TEXT_ALIGN_LEFT or @@ -186,16 +172,6 @@ class GFX_EXPORT Canvas { SkColor halo_color, const Rect& display_rect, int flags); - // Obsolete version. Use the above version which takes FontList. - void DrawStringWithHalo(const base::string16& text, - const Font& font, - SkColor text_color, - SkColor halo_color, - int x, - int y, - int w, - int h, - int flags); // Extracts an ImageSkiaRep from the contents of this canvas. ImageSkiaRep ExtractImageRep() const; @@ -218,13 +194,15 @@ class GFX_EXPORT Canvas { // call Restore() more times than Save*(). void Restore(); - // Adds |rect| to the current clip. Returns true if the resulting clip is - // non-empty. - bool ClipRect(const Rect& rect); + // Adds |rect| to the current clip. + void ClipRect(const Rect& rect); + + // Adds |path| to the current clip. |do_anti_alias| is true if the clip + // should be antialiased. + void ClipPath(const SkPath& path, bool do_anti_alias); - // Adds |path| to the current clip. Returns true if the resulting clip is - // non-empty. - bool ClipPath(const SkPath& path); + // Returns true if the current clip is empty. + bool IsClipEmpty() const; // Returns the bounds of the current clip (in local coordinates) in the // |bounds| parameter, and returns true if it is non empty. @@ -339,6 +317,22 @@ class GFX_EXPORT Canvas { bool filter, const SkPaint& paint); + // Same as the DrawImageInt functions above. Difference being this does not + // do any scaling, i.e. it assumes that the source/destination/image, etc are + // in pixels. It does translate the destination rectangle to ensure that the + // image is displayed at the correct pixel coordinates. + void DrawImageIntInPixel(const ImageSkia& image, + int src_x, + int src_y, + int src_w, + int src_h, + int dest_x, + int dest_y, + int dest_w, + int dest_h, + bool filter, + const SkPaint& paint); + // Draws an |image| with the top left corner at |x| and |y|, clipped to // |path|. // Parameters are specified relative to current canvas scale not in pixels. @@ -356,18 +350,6 @@ class GFX_EXPORT Canvas { const FontList& font_list, SkColor color, const Rect& display_rect); - // Obsolete versions. Use the above versions which take FontList. - void DrawStringInt(const base::string16& text, - const Font& font, - SkColor color, - int x, - int y, - int w, - int h); - void DrawStringInt(const base::string16& text, - const Font& font, - SkColor color, - const Rect& display_rect); // Draws text with the specified color, fonts and location. The last argument // specifies flags for how the text should be rendered. It can be one of @@ -377,17 +359,8 @@ class GFX_EXPORT Canvas { SkColor color, const Rect& display_rect, int flags); - // Obsolete version. Use the above version which takes FontList. - void DrawStringInt(const base::string16& text, - const Font& font, - SkColor color, - int x, - int y, - int w, - int h, - int flags); - - // Similar to above DrawStringInt method but with text shadows support. + + // Similar to above DrawStringRect method but with text shadows support. // Currently it's only implemented for canvas skia. Specifying a 0 line_height // will cause the default height to be used. void DrawStringRectWithShadows(const base::string16& text, @@ -397,14 +370,6 @@ class GFX_EXPORT Canvas { int line_height, int flags, const ShadowValues& shadows); - // Obsolete version. Use the above version which takes FontList. - void DrawStringWithShadows(const base::string16& text, - const Font& font, - SkColor color, - const Rect& text_bounds, - int line_height, - int flags, - const ShadowValues& shadows); // Draws a dotted gray rectangle used for focus purposes. void DrawFocusRect(const Rect& rect); @@ -449,20 +414,12 @@ class GFX_EXPORT Canvas { // Apply transformation on the canvas. void Transform(const Transform& transform); - // Draws the given string with the beginning or the end using a fade gradient. - void DrawFadeTruncatingStringRect( - const base::string16& text, - TruncateFadeMode truncate_mode, - const FontList& font_list, - SkColor color, - const Rect& display_rect); - void DrawFadeTruncatingStringRectWithFlags( - const base::string16& text, - TruncateFadeMode truncate_mode, - const FontList& font_list, - SkColor color, - const Rect& display_rect, - int flags); + // Draws the given string with a fade gradient at the end. + void DrawFadedString(const base::string16& text, + const FontList& font_list, + SkColor color, + const Rect& display_rect, + int flags); skia::PlatformCanvas* platform_canvas() const { return owned_canvas_.get(); } SkCanvas* sk_canvas() const { return canvas_; } @@ -475,16 +432,22 @@ class GFX_EXPORT Canvas { bool IntersectsClipRectInt(int x, int y, int w, int h); bool IntersectsClipRect(const Rect& rect); - // Returns the image rep which best matches the canvas |image_scale_|. - // Returns a null image rep if |image| contains no image reps. - // Builds mip map for returned image rep if necessary. - // - // An optional additional user defined scale can be provided. - const ImageSkiaRep& GetImageRepToPaint(const ImageSkia& image) const; - const ImageSkiaRep& GetImageRepToPaint( - const ImageSkia& image, - float user_defined_scale_factor_x, - float user_defined_scale_factor_y) const; + // Helper for the DrawImageInt functions declared above. The |pixel| + // parameter if true indicates that the bounds and the image are to + // be assumed to be in pixels, i.e. no scaling needs to be performed. + void DrawImageIntHelper(const ImageSkia& image, + int src_x, + int src_y, + int src_w, + int src_h, + int dest_x, + int dest_y, + int dest_w, + int dest_h, + bool filter, + const SkPaint& paint, + float image_scale, + bool pixel); // The device scale factor at which drawing on this canvas occurs. // An additional scale can be applied via Canvas::Scale(). However, diff --git a/chromium/ui/gfx/canvas_paint_gtk.cc b/chromium/ui/gfx/canvas_paint_gtk.cc deleted file mode 100644 index aef6fc982e3..00000000000 --- a/chromium/ui/gfx/canvas_paint_gtk.cc +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/basictypes.h" -#include "base/compiler_specific.h" -#include "ui/gfx/canvas.h" -#include "ui/gfx/canvas_skia_paint.h" -#include "ui/gfx/rect.h" - -namespace gfx { - -// CanvasSkiaPaint - -CanvasSkiaPaint::CanvasSkiaPaint(GdkEventExpose* event) - : context_(NULL), - window_(event->window), - region_(gdk_region_copy(event->region)), - composite_alpha_(false) { - Init(true); -} - -CanvasSkiaPaint::CanvasSkiaPaint(GdkEventExpose* event, bool opaque) - : context_(NULL), - window_(event->window), - region_(gdk_region_copy(event->region)), - composite_alpha_(false) { - Init(opaque); -} - -CanvasSkiaPaint::~CanvasSkiaPaint() { - if (!is_empty()) { - platform_canvas()->restoreToCount(1); - - // Blit the dirty rect to the window. - CHECK(window_); - cairo_t* cr = gdk_cairo_create(window_); - CHECK(cr); - if (composite_alpha_) - cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); - cairo_surface_t* source_surface = cairo_get_target(context_); - CHECK(source_surface); - // Flush cairo's cache of the surface. - cairo_surface_mark_dirty(source_surface); - GdkRectangle bounds = rectangle(); - cairo_set_source_surface(cr, source_surface, bounds.x, bounds.y); - gdk_cairo_region(cr, region_); - cairo_fill(cr); - cairo_destroy(cr); - } - - gdk_region_destroy(region_); -} - -void CanvasSkiaPaint::Init(bool opaque) { - GdkRectangle bounds = rectangle(); - RecreateBackingCanvas(Size(bounds.width, bounds.height), 1.0f, opaque); - - skia::PlatformCanvas* canvas = platform_canvas(); - - // Need to translate so that the dirty region appears at the origin of the - // surface. - canvas->translate(-SkIntToScalar(bounds.x), -SkIntToScalar(bounds.y)); - - context_ = skia::BeginPlatformPaint(canvas); -} - -// CanvasSkiaPaintCairo - -CanvasSkiaPaintCairo::CanvasSkiaPaintCairo(cairo_t* cairo, - Size size, - bool opaque) - : context_(NULL), - dest_(cairo), - size_(size), - composite_alpha_(false) { - CHECK(dest_); - Init(opaque); -} - -CanvasSkiaPaintCairo::~CanvasSkiaPaintCairo() { - if (!is_empty()) { - platform_canvas()->restoreToCount(1); - - // Blit the dirty rect to the window. - if (composite_alpha_) - cairo_set_operator(dest_, CAIRO_OPERATOR_SOURCE); - cairo_surface_t* source_surface = cairo_get_target(context_); - CHECK(source_surface); - // Flush cairo's cache of the surface. - cairo_surface_mark_dirty(source_surface); - cairo_set_source_surface(dest_, source_surface, 0, 0); - GdkRectangle bounds = {0, 0, size_.width(), size_.height()}; - gdk_cairo_rectangle(dest_, &bounds); - cairo_fill(dest_); - } -} - -void CanvasSkiaPaintCairo::Init(bool opaque) { - RecreateBackingCanvas(size_, 1.0f, opaque); - - context_ = skia::BeginPlatformPaint(platform_canvas()); -} - -} // namespace gfx - - diff --git a/chromium/ui/gfx/canvas_paint_gtk.h b/chromium/ui/gfx/canvas_paint_gtk.h deleted file mode 100644 index 487db79f47a..00000000000 --- a/chromium/ui/gfx/canvas_paint_gtk.h +++ /dev/null @@ -1,102 +0,0 @@ - -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_GFX_CANVAS_PAINT_LINUX_H_ -#define UI_GFX_CANVAS_PAINT_LINUX_H_ - -#include "base/logging.h" -#include "skia/ext/platform_canvas.h" -#include "ui/gfx/canvas.h" -#include <gdk/gdk.h> - -namespace gfx { - -// A class designed to translate skia painting into a region in a GdkWindow. -// On construction, it will set up a context for painting into, and on -// destruction, it will commit it to the GdkWindow. -// Note: The created context is always inialized to (0, 0, 0, 0). -class GFX_EXPORT CanvasSkiaPaint : public Canvas { - public: - // This constructor assumes the result is opaque. - explicit CanvasSkiaPaint(GdkEventExpose* event); - CanvasSkiaPaint(GdkEventExpose* event, bool opaque); - virtual ~CanvasSkiaPaint(); - - // Sets whether the bitmap is composited in such a way that the alpha channel - // is honored. This is only useful if you've enabled an RGBA colormap on the - // widget. The default is false. - void set_composite_alpha(bool composite_alpha) { - composite_alpha_ = composite_alpha; - } - - // Returns true if the invalid region is empty. The caller should call this - // function to determine if anything needs painting. - bool is_empty() const { - return gdk_region_empty(region_); - } - - GdkRectangle rectangle() const { - GdkRectangle bounds; - gdk_region_get_clipbox(region_, &bounds); - return bounds; - } - - private: - void Init(bool opaque); - - cairo_t* context_; - GdkWindow* window_; - GdkRegion* region_; - // See description above setter. - bool composite_alpha_; - - // Disallow copy and assign. - CanvasSkiaPaint(const CanvasSkiaPaint&); - CanvasSkiaPaint& operator=(const CanvasSkiaPaint&); -}; - -// A class designed to translate skia painting into a region in a Cairo context. -// On construction, it will set up a context for painting into, and on -// destruction, it will commit it to the Cairo context. If there are any -// transformations applied to the Cairo context, they will affect the drawing. -class GFX_EXPORT CanvasSkiaPaintCairo : public Canvas { - public: - CanvasSkiaPaintCairo(cairo_t* cairo, Size size, bool opaque); - virtual ~CanvasSkiaPaintCairo(); - - // Sets whether the bitmap is composited in such a way that the alpha channel - // is honored. This is only useful if you've enabled an RGBA colormap on the - // widget. The default is false. - void set_composite_alpha(bool composite_alpha) { - composite_alpha_ = composite_alpha; - } - - // Returns true if size of the drawing region is empty. The caller should call - // this function to determine if anything needs painting. - bool is_empty() const { - return size_.IsEmpty(); - } - - Size size() const { - return size_; - } - - private: - void Init(bool opaque); - - cairo_t* context_; - cairo_t* dest_; - Size size_; - // See description above setter. - bool composite_alpha_; - - // Disallow copy and assign. - CanvasSkiaPaintCairo(const CanvasSkiaPaintCairo&); - CanvasSkiaPaintCairo& operator=(const CanvasSkiaPaintCairo&); -}; - -} // namespace gfx - -#endif // UI_GFX_CANVAS_PAINT_LINUX_H_ diff --git a/chromium/ui/gfx/canvas_skia.cc b/chromium/ui/gfx/canvas_skia.cc index 1ad60ba2b04..8d1311faf39 100644 --- a/chromium/ui/gfx/canvas_skia.cc +++ b/chromium/ui/gfx/canvas_skia.cc @@ -84,7 +84,7 @@ bool PixelShouldGetHalo(const SkBitmap& bitmap, } // Strips accelerator character prefixes in |text| if needed, based on |flags|. -// Returns a range in |text| to underline or gfx::Range::InvalidRange() if +// Returns a range in |text| to underline or Range::InvalidRange() if // underlining is not needed. Range StripAcceleratorChars(int flags, base::string16* text) { if (flags & (Canvas::SHOW_PREFIX | Canvas::HIDE_PREFIX)) { @@ -105,7 +105,7 @@ void ElideTextAndAdjustRange(const FontList& font_list, Range* range) { const base::char16 start_char = (range->IsValid() ? text->at(range->start()) : 0); - *text = gfx::ElideText(*text, font_list, width, gfx::ELIDE_AT_END); + *text = ElideText(*text, font_list, width, ELIDE_TAIL); if (!range->IsValid()) return; if (range->start() >= text->length() || @@ -171,17 +171,16 @@ void Canvas::SizeStringFloat(const base::string16& text, #endif if ((flags & MULTI_LINE) && *width != 0) { - gfx::WordWrapBehavior wrap_behavior = gfx::TRUNCATE_LONG_WORDS; + WordWrapBehavior wrap_behavior = TRUNCATE_LONG_WORDS; if (flags & CHARACTER_BREAK) - wrap_behavior = gfx::WRAP_LONG_WORDS; + wrap_behavior = WRAP_LONG_WORDS; else if (!(flags & NO_ELLIPSIS)) - wrap_behavior = gfx::ELIDE_LONG_WORDS; + wrap_behavior = ELIDE_LONG_WORDS; Rect rect(*width, INT_MAX); std::vector<base::string16> strings; - gfx::ElideRectangleText(adjusted_text, font_list, - rect.width(), rect.height(), - wrap_behavior, &strings); + ElideRectangleText(adjusted_text, font_list, rect.width(), rect.height(), + wrap_behavior, &strings); scoped_ptr<RenderText> render_text(RenderText::CreateInstance()); UpdateRenderText(rect, base::string16(), font_list, flags, 0, render_text.get()); @@ -230,7 +229,7 @@ void Canvas::DrawStringRectWithShadows(const base::string16& text, Rect clip_rect(text_bounds); clip_rect.Inset(ShadowValue::GetMargin(shadows)); - canvas_->save(SkCanvas::kClip_SaveFlag); + canvas_->save(); ClipRect(clip_rect); Rect rect(text_bounds); @@ -241,21 +240,18 @@ void Canvas::DrawStringRectWithShadows(const base::string16& text, #endif scoped_ptr<RenderText> render_text(RenderText::CreateInstance()); - render_text->SetTextShadows(shadows); + render_text->set_shadows(shadows); if (flags & MULTI_LINE) { - gfx::WordWrapBehavior wrap_behavior = gfx::IGNORE_LONG_WORDS; + WordWrapBehavior wrap_behavior = IGNORE_LONG_WORDS; if (flags & CHARACTER_BREAK) - wrap_behavior = gfx::WRAP_LONG_WORDS; + wrap_behavior = WRAP_LONG_WORDS; else if (!(flags & NO_ELLIPSIS)) - wrap_behavior = gfx::ELIDE_LONG_WORDS; + wrap_behavior = ELIDE_LONG_WORDS; std::vector<base::string16> strings; - gfx::ElideRectangleText(adjusted_text, - font_list, - text_bounds.width(), text_bounds.height(), - wrap_behavior, - &strings); + ElideRectangleText(adjusted_text, font_list, text_bounds.width(), + text_bounds.height(), wrap_behavior, &strings); for (size_t i = 0; i < strings.size(); i++) { Range range = StripAcceleratorChars(flags, &strings[i]); @@ -294,16 +290,14 @@ void Canvas::DrawStringRectWithShadows(const base::string16& text, if (elide_text) { render_text->SetText(adjusted_text); if (render_text->GetTextDirection() == base::i18n::LEFT_TO_RIGHT) { - render_text->set_fade_tail(true); + render_text->SetElideBehavior(FADE_TAIL); elide_text = false; } } #endif if (elide_text) { - ElideTextAndAdjustRange(font_list, - text_bounds.width(), - &adjusted_text, + ElideTextAndAdjustRange(font_list, text_bounds.width(), &adjusted_text, &range); } @@ -311,7 +305,6 @@ void Canvas::DrawStringRectWithShadows(const base::string16& text, render_text.get()); const int text_height = render_text->GetStringSize().height(); - // Center the text vertically. rect += Vector2d(0, (text_bounds.height() - text_height) / 2); rect.set_height(text_height); render_text->SetDisplayRect(rect); @@ -371,60 +364,44 @@ void Canvas::DrawStringRectWithHalo(const base::string16& text, DrawImageInt(text_image, display_rect.x() - 1, display_rect.y() - 1); } -void Canvas::DrawFadeTruncatingStringRect( - const base::string16& text, - TruncateFadeMode truncate_mode, - const FontList& font_list, - SkColor color, - const Rect& display_rect) { - DrawFadeTruncatingStringRectWithFlags( - text, truncate_mode, font_list, color, display_rect, NO_ELLIPSIS); -} - -void Canvas::DrawFadeTruncatingStringRectWithFlags( - const base::string16& text, - TruncateFadeMode truncate_mode, - const FontList& font_list, - SkColor color, - const Rect& display_rect, - int flags) { +void Canvas::DrawFadedString(const base::string16& text, + const FontList& font_list, + SkColor color, + const Rect& display_rect, + int flags) { // If the whole string fits in the destination then just draw it directly. if (GetStringWidth(text, font_list) <= display_rect.width()) { DrawStringRectWithFlags(text, font_list, color, display_rect, flags); return; } - scoped_ptr<RenderText> render_text(RenderText::CreateInstance()); - const bool is_rtl = base::i18n::GetFirstStrongCharacterDirection(text) == - base::i18n::RIGHT_TO_LEFT; - - switch (truncate_mode) { - case TruncateFadeTail: - render_text->set_fade_tail(true); - if (is_rtl) - flags |= TEXT_ALIGN_RIGHT; - break; - case TruncateFadeHead: - render_text->set_fade_head(true); - if (!is_rtl) - flags |= TEXT_ALIGN_RIGHT; - break; - } - - // Default to left alignment unless right alignment was chosen above. - if (!(flags & TEXT_ALIGN_RIGHT)) + // Align with forced content directionality, overriding alignment flags. + if (flags & FORCE_RTL_DIRECTIONALITY) { + flags &= ~(TEXT_ALIGN_CENTER | TEXT_ALIGN_LEFT); + flags |= TEXT_ALIGN_RIGHT; + } else if (flags & FORCE_LTR_DIRECTIONALITY) { + flags &= ~(TEXT_ALIGN_CENTER | TEXT_ALIGN_RIGHT); flags |= TEXT_ALIGN_LEFT; + } else if (!(flags & TEXT_ALIGN_LEFT) && !(flags & TEXT_ALIGN_RIGHT)) { + // Also align with content directionality instead of fading both ends. + flags &= ~TEXT_ALIGN_CENTER; + const bool is_rtl = base::i18n::GetFirstStrongCharacterDirection(text) == + base::i18n::RIGHT_TO_LEFT; + flags |= is_rtl ? TEXT_ALIGN_RIGHT : TEXT_ALIGN_LEFT; + } + flags |= NO_ELLIPSIS; + scoped_ptr<RenderText> render_text(RenderText::CreateInstance()); Rect rect = display_rect; UpdateRenderText(rect, text, font_list, flags, color, render_text.get()); + render_text->SetElideBehavior(FADE_TAIL); const int line_height = render_text->GetStringSize().height(); - // Center the text vertically. rect += Vector2d(0, (display_rect.height() - line_height) / 2); rect.set_height(line_height); render_text->SetDisplayRect(rect); - canvas_->save(SkCanvas::kClip_SaveFlag); + canvas_->save(); ClipRect(display_rect); render_text->Draw(this); canvas_->restore(); diff --git a/chromium/ui/gfx/canvas_skia_paint.h b/chromium/ui/gfx/canvas_skia_paint.h index 3f21f5cc410..e9e22fff44e 100644 --- a/chromium/ui/gfx/canvas_skia_paint.h +++ b/chromium/ui/gfx/canvas_skia_paint.h @@ -12,12 +12,8 @@ #include "ui/gfx/canvas_paint_win.h" #elif defined(__APPLE__) #include "ui/gfx/canvas_paint_mac.h" -#elif defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__sun) -#if defined(TOOLKIT_GTK) -#include "ui/gfx/canvas_paint_gtk.h" #else #error "No canvas paint for this platform" #endif -#endif #endif // UI_GFX_CANVAS_SKIA_PAINT_H_ diff --git a/chromium/ui/gfx/canvas_unittest.cc b/chromium/ui/gfx/canvas_unittest.cc index 21b9f51e57e..806e8f0e7a5 100644 --- a/chromium/ui/gfx/canvas_unittest.cc +++ b/chromium/ui/gfx/canvas_unittest.cc @@ -2,32 +2,34 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "ui/gfx/canvas.h" + #include <limits> #include "base/strings/utf_string_conversions.h" #include "testing/gtest/include/gtest/gtest.h" -#include "ui/gfx/canvas.h" -#include "ui/gfx/font.h" +#include "ui/gfx/font_list.h" namespace gfx { class CanvasTest : public testing::Test { protected: int GetStringWidth(const char *text) { - return Canvas::GetStringWidth(UTF8ToUTF16(text), font_); + return Canvas::GetStringWidth(base::UTF8ToUTF16(text), font_list_); } gfx::Size SizeStringInt(const char *text, int width, int line_height) { - base::string16 text16 = UTF8ToUTF16(text); + base::string16 text16 = base::UTF8ToUTF16(text); int height = 0; int flags = - (text16.find('\n') != base::string16::npos) ? Canvas::MULTI_LINE : 0; - Canvas::SizeStringInt(text16, font_, &width, &height, line_height, flags); + (text16.find('\n') != base::string16::npos) ? Canvas::MULTI_LINE : 0; + Canvas::SizeStringInt(text16, font_list_, &width, &height, line_height, + flags); return gfx::Size(width, height); } private: - gfx::Font font_; + FontList font_list_; }; TEST_F(CanvasTest, StringWidth) { diff --git a/chromium/ui/gfx/codec/png_codec.cc b/chromium/ui/gfx/codec/png_codec.cc index b3f4345343f..d098eb4ee18 100644 --- a/chromium/ui/gfx/codec/png_codec.cc +++ b/chromium/ui/gfx/codec/png_codec.cc @@ -678,15 +678,29 @@ bool EncodeWithCompressionLevel(const unsigned char* input, break; case PNGCodec::FORMAT_SkBitmap: - input_color_components = 4; - if (discard_transparency) { - output_color_components = 3; - png_output_color_type = PNG_COLOR_TYPE_RGB; - converter = ConvertSkiatoRGB; + // Compare row_byte_width and size.width() to detect the format of + // SkBitmap. kA8_Config (1bpp) and kARGB_8888_Config (4bpp) are the two + // supported formats. + if (row_byte_width < 4 * size.width()) { + // Not 4bpp, so must be 1bpp. + // Ignore discard_transparency - it doesn't make sense in this context, + // since alpha is the only thing we have and it needs to be used for + // color intensity. + input_color_components = 1; + output_color_components = 1; + png_output_color_type = PNG_COLOR_TYPE_GRAY; + // |converter| is left as null } else { - output_color_components = 4; - png_output_color_type = PNG_COLOR_TYPE_RGB_ALPHA; - converter = ConvertSkiatoRGBA; + input_color_components = 4; + if (discard_transparency) { + output_color_components = 3; + png_output_color_type = PNG_COLOR_TYPE_RGB; + converter = ConvertSkiatoRGB; + } else { + output_color_components = 4; + png_output_color_type = PNG_COLOR_TYPE_RGB_ALPHA; + converter = ConvertSkiatoRGBA; + } } break; @@ -719,12 +733,38 @@ bool EncodeWithCompressionLevel(const unsigned char* input, return success; } +bool InternalEncodeSkBitmap(const SkBitmap& input, + bool discard_transparency, + int compression_level, + std::vector<unsigned char>* output) { + if (input.empty() || input.isNull()) + return false; + int bpp = input.bytesPerPixel(); + DCHECK(bpp == 1 || bpp == 4); // We support kA8_Config and kARGB_8888_Config. + + SkAutoLockPixels lock_input(input); + unsigned char* inputAddr = bpp == 1 ? + reinterpret_cast<unsigned char*>(input.getAddr8(0, 0)) : + reinterpret_cast<unsigned char*>(input.getAddr32(0, 0)); // bpp = 4 + return EncodeWithCompressionLevel( + inputAddr, + PNGCodec::FORMAT_SkBitmap, + Size(input.width(), input.height()), + static_cast<int>(input.rowBytes()), + discard_transparency, + std::vector<PNGCodec::Comment>(), + compression_level, + output); +} + } // namespace // static -bool PNGCodec::Encode(const unsigned char* input, ColorFormat format, - const Size& size, int row_byte_width, +bool PNGCodec::Encode(const unsigned char* input, + ColorFormat format, + const Size& size, + int row_byte_width, bool discard_transparency, const std::vector<Comment>& comments, std::vector<unsigned char>* output) { @@ -742,37 +782,29 @@ bool PNGCodec::Encode(const unsigned char* input, ColorFormat format, bool PNGCodec::EncodeBGRASkBitmap(const SkBitmap& input, bool discard_transparency, std::vector<unsigned char>* output) { - static const int bbp = 4; - - if (input.empty()) - return false; - DCHECK_EQ(input.bytesPerPixel(), bbp); - DCHECK_GE(static_cast<int>(input.rowBytes()), input.width() * bbp); + return InternalEncodeSkBitmap(input, + discard_transparency, + Z_DEFAULT_COMPRESSION, + output); +} - SkAutoLockPixels lock_input(input); - return Encode(reinterpret_cast<unsigned char*>(input.getAddr32(0, 0)), - FORMAT_SkBitmap, Size(input.width(), input.height()), - static_cast<int>(input.rowBytes()), discard_transparency, - std::vector<Comment>(), output); +// static +bool PNGCodec::EncodeA8SkBitmap(const SkBitmap& input, + std::vector<unsigned char>* output) { + return InternalEncodeSkBitmap(input, + false, + Z_DEFAULT_COMPRESSION, + output); } // static bool PNGCodec::FastEncodeBGRASkBitmap(const SkBitmap& input, bool discard_transparency, std::vector<unsigned char>* output) { - static const int bbp = 4; - - if (input.empty()) - return false; - DCHECK_EQ(input.bytesPerPixel(), bbp); - DCHECK_GE(static_cast<int>(input.rowBytes()), input.width() * bbp); - - SkAutoLockPixels lock_input(input); - return EncodeWithCompressionLevel( - reinterpret_cast<unsigned char*>(input.getAddr32(0, 0)), - FORMAT_SkBitmap, Size(input.width(), input.height()), - static_cast<int>(input.rowBytes()), discard_transparency, - std::vector<Comment>(), Z_BEST_SPEED, output); + return InternalEncodeSkBitmap(input, + discard_transparency, + Z_BEST_SPEED, + output); } PNGCodec::Comment::Comment(const std::string& k, const std::string& t) diff --git a/chromium/ui/gfx/codec/png_codec.h b/chromium/ui/gfx/codec/png_codec.h index abb3eaab32c..5f849c462fc 100644 --- a/chromium/ui/gfx/codec/png_codec.h +++ b/chromium/ui/gfx/codec/png_codec.h @@ -37,8 +37,10 @@ class GFX_EXPORT PNGCodec { // This is the default Windows DIB order. FORMAT_BGRA, - // 4 bytes per pixel, in pre-multiplied kARGB_8888_Config format. For use - // with directly writing to a skia bitmap. + // SkBitmap format. For Encode() kARGB_8888_Config (4 bytes per pixel) and + // kA8_Config (1 byte per pixel) formats are supported. kA8_Config gets + // encoded into a grayscale PNG treating alpha as the color intensity. + // For Decode() kARGB_8888_Config is always used. FORMAT_SkBitmap }; @@ -77,9 +79,10 @@ class GFX_EXPORT PNGCodec { std::vector<unsigned char>* output); // Call PNGCodec::Encode on the supplied SkBitmap |input|, which is assumed - // to be BGRA, 32 bits per pixel. The params |discard_transparency| and - // |output| are passed directly to Encode; refer to Encode for more - // information. During the call, an SkAutoLockPixels lock is held on |input|. + // to be kARGB_8888_Config, 32 bits per pixel. The params + // |discard_transparency| and |output| are passed directly to Encode; refer to + // Encode for more information. During the call, an SkAutoLockPixels lock + // is held on |input|. static bool EncodeBGRASkBitmap(const SkBitmap& input, bool discard_transparency, std::vector<unsigned char>* output); @@ -91,6 +94,14 @@ class GFX_EXPORT PNGCodec { bool discard_transparency, std::vector<unsigned char>* output); + // Call PNGCodec::Encode on the supplied SkBitmap |input|, which is assumed + // to be kA8_Config, 8 bits per pixel. The bitmap is encoded as a grayscale + // PNG with alpha used for color intensity. The |output| param is passed + // directly to Encode; refer to Encode for more information. During the call, + // an SkAutoLockPixels lock is held on |input|. + static bool EncodeA8SkBitmap(const SkBitmap& input, + std::vector<unsigned char>* output); + // Decodes the PNG data contained in input of length input_size. The // decoded data will be placed in *output with the dimensions in *w and *h // on success (returns true). This data will be written in the 'format' diff --git a/chromium/ui/gfx/codec/png_codec_unittest.cc b/chromium/ui/gfx/codec/png_codec_unittest.cc index f1654d9d363..37816807ba9 100644 --- a/chromium/ui/gfx/codec/png_codec_unittest.cc +++ b/chromium/ui/gfx/codec/png_codec_unittest.cc @@ -249,14 +249,29 @@ bool NonAlphaColorsClose(uint32_t a, uint32_t b) { abs(static_cast<int>(SkColorGetR(a) - SkColorGetR(b))) < 2; } -void MakeTestSkBitmap(int w, int h, SkBitmap* bmp) { +// Returns true if the BGRA 32-bit SkColor specified by |a| is equivalent to the +// 8-bit Gray color specified by |b|. +bool BGRAGrayEqualsA8Gray(uint32_t a, uint8_t b) { + return SkColorGetB(a) == b && SkColorGetG(a) == b && + SkColorGetR(a) == b && SkColorGetA(a) == 255; +} + +void MakeTestBGRASkBitmap(int w, int h, SkBitmap* bmp) { bmp->setConfig(SkBitmap::kARGB_8888_Config, w, h); bmp->allocPixels(); uint32_t* src_data = bmp->getAddr32(0, 0); - for (int i = 0; i < w * h; i++) { + for (int i = 0; i < w * h; i++) src_data[i] = SkPreMultiplyARGB(i % 255, i % 250, i % 245, i % 240); - } +} + +void MakeTestA8SkBitmap(int w, int h, SkBitmap* bmp) { + bmp->setConfig(SkBitmap::kA8_Config, w, h); + bmp->allocPixels(); + + uint8_t* src_data = bmp->getAddr8(0, 0); + for (int i = 0; i < w * h; i++) + src_data[i] = i % 255; } TEST(PNGCodec, EncodeDecodeRGB) { @@ -1017,7 +1032,7 @@ TEST(PNGCodec, EncodeBGRASkBitmap) { const int w = 20, h = 20; SkBitmap original_bitmap; - MakeTestSkBitmap(w, h, &original_bitmap); + MakeTestBGRASkBitmap(w, h, &original_bitmap); // Encode the bitmap. std::vector<unsigned char> encoded; @@ -1040,11 +1055,35 @@ TEST(PNGCodec, EncodeBGRASkBitmap) { } } +TEST(PNGCodec, EncodeA8SkBitmap) { + const int w = 20, h = 20; + + SkBitmap original_bitmap; + MakeTestA8SkBitmap(w, h, &original_bitmap); + + // Encode the bitmap. + std::vector<unsigned char> encoded; + EXPECT_TRUE(PNGCodec::EncodeA8SkBitmap(original_bitmap, &encoded)); + + // Decode the encoded string. + SkBitmap decoded_bitmap; + EXPECT_TRUE(PNGCodec::Decode(&encoded.front(), encoded.size(), + &decoded_bitmap)); + + for (int x = 0; x < w; x++) { + for (int y = 0; y < h; y++) { + uint8_t original_pixel = *original_bitmap.getAddr8(x, y); + uint32_t decoded_pixel = *decoded_bitmap.getAddr32(x, y); + EXPECT_TRUE(BGRAGrayEqualsA8Gray(decoded_pixel, original_pixel)); + } + } +} + TEST(PNGCodec, EncodeBGRASkBitmapDiscardTransparency) { const int w = 20, h = 20; SkBitmap original_bitmap; - MakeTestSkBitmap(w, h, &original_bitmap); + MakeTestBGRASkBitmap(w, h, &original_bitmap); // Encode the bitmap. std::vector<unsigned char> encoded; @@ -1121,7 +1160,7 @@ TEST(PNGCodec, EncodeDecodeWithVaryingCompressionLevels) { // create an image with known values, a must be opaque because it will be // lost during encoding SkBitmap original_bitmap; - MakeTestSkBitmap(w, h, &original_bitmap); + MakeTestBGRASkBitmap(w, h, &original_bitmap); // encode std::vector<unsigned char> encoded_normal; diff --git a/chromium/ui/gfx/color_analysis.cc b/chromium/ui/gfx/color_analysis.cc index f8987cfb546..5cae9b6d4cc 100644 --- a/chromium/ui/gfx/color_analysis.cc +++ b/chromium/ui/gfx/color_analysis.cc @@ -13,14 +13,17 @@ #include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkUnPreMultiply.h" #include "ui/gfx/codec/png_codec.h" +#include "ui/gfx/color_utils.h" +namespace color_utils { namespace { // RGBA KMean Constants const uint32_t kNumberOfClusters = 4; const int kNumberOfIterations = 50; -const uint32_t kMaxBrightness = 665; -const uint32_t kMinDarkness = 100; + +const HSL kDefaultLowerHSLBound = {-1, -1, 0.15}; +const HSL kDefaultUpperHSLBound = {-1, -1, 0.85}; // Background Color Modification Constants const SkColor kDefaultBgColor = SK_ColorWHITE; @@ -139,8 +142,6 @@ void UnPreMultiply(const SkBitmap& bitmap, uint32_t* buffer, int buffer_size) { } // namespace -namespace color_utils { - KMeanImageSampler::KMeanImageSampler() { } @@ -214,8 +215,8 @@ SkColor FindClosestColor(const uint8_t* image, SkColor CalculateKMeanColorOfBuffer(uint8_t* decoded_data, int img_width, int img_height, - uint32_t darkness_limit, - uint32_t brightness_limit, + const HSL& lower_bound, + const HSL& upper_bound, KMeanImageSampler* sampler) { SkColor color = kDefaultBgColor; if (img_width > 0 && img_height > 0) { @@ -331,21 +332,19 @@ SkColor CalculateKMeanColorOfBuffer(uint8_t* decoded_data, cluster != clusters.end(); ++cluster) { uint8_t r, g, b; cluster->GetCentroid(&r, &g, &b); - // Sum the RGB components to determine if the color is too bright or too - // dark. - // TODO (dtrainor): Look into using HSV here instead. This approximation - // might be fine though. - uint32_t summed_color = r + g + b; - if (summed_color < brightness_limit && summed_color > darkness_limit) { + SkColor current_color = SkColorSetARGB(SK_AlphaOPAQUE, r, g, b); + HSL hsl; + SkColorToHSL(current_color, &hsl); + if (IsWithinHSLRange(hsl, lower_bound, upper_bound)) { // If we found a valid color just set it and break. We don't want to // check the other ones. - color = SkColorSetARGB(0xFF, r, g, b); + color = current_color; break; } else if (cluster == clusters.begin()) { // We haven't found a valid color, but we are at the first color so // set the color anyway to make sure we at least have a value here. - color = SkColorSetARGB(0xFF, r, g, b); + color = current_color; } } } @@ -356,16 +355,15 @@ SkColor CalculateKMeanColorOfBuffer(uint8_t* decoded_data, } SkColor CalculateKMeanColorOfPNG(scoped_refptr<base::RefCountedMemory> png, - uint32_t darkness_limit, - uint32_t brightness_limit, + const HSL& lower_bound, + const HSL& upper_bound, KMeanImageSampler* sampler) { int img_width = 0; int img_height = 0; std::vector<uint8_t> decoded_data; SkColor color = kDefaultBgColor; - if (png.get() && - png->size() && + if (png.get() && png->size() && gfx::PNGCodec::Decode(png->front(), png->size(), gfx::PNGCodec::FORMAT_BGRA, @@ -375,14 +373,23 @@ SkColor CalculateKMeanColorOfPNG(scoped_refptr<base::RefCountedMemory> png, return CalculateKMeanColorOfBuffer(&decoded_data[0], img_width, img_height, - darkness_limit, - brightness_limit, + lower_bound, + upper_bound, sampler); } return color; } -SkColor CalculateKMeanColorOfBitmap(const SkBitmap& bitmap) { +SkColor CalculateKMeanColorOfPNG(scoped_refptr<base::RefCountedMemory> png) { + GridSampler sampler; + return CalculateKMeanColorOfPNG( + png, kDefaultLowerHSLBound, kDefaultUpperHSLBound, &sampler); +} + +SkColor CalculateKMeanColorOfBitmap(const SkBitmap& bitmap, + const HSL& lower_bound, + const HSL& upper_bound, + KMeanImageSampler* sampler) { // SkBitmap uses pre-multiplied alpha but the KMean clustering function // above uses non-pre-multiplied alpha. Transform the bitmap before we // analyze it because the function reads each pixel multiple times. @@ -390,15 +397,18 @@ SkColor CalculateKMeanColorOfBitmap(const SkBitmap& bitmap) { scoped_ptr<uint32_t[]> image(new uint32_t[pixel_count]); UnPreMultiply(bitmap, image.get(), pixel_count); + return CalculateKMeanColorOfBuffer(reinterpret_cast<uint8_t*>(image.get()), + bitmap.width(), + bitmap.height(), + lower_bound, + upper_bound, + sampler); +} + +SkColor CalculateKMeanColorOfBitmap(const SkBitmap& bitmap) { GridSampler sampler; - SkColor color = CalculateKMeanColorOfBuffer( - reinterpret_cast<uint8_t*>(image.get()), - bitmap.width(), - bitmap.height(), - kMinDarkness, - kMaxBrightness, - &sampler); - return color; + return CalculateKMeanColorOfBitmap( + bitmap, kDefaultLowerHSLBound, kDefaultUpperHSLBound, &sampler); } gfx::Matrix3F ComputeColorCovariance(const SkBitmap& bitmap) { @@ -409,7 +419,7 @@ gfx::Matrix3F ComputeColorCovariance(const SkBitmap& bitmap) { return covariance; // Assume ARGB_8888 format. - DCHECK(bitmap.config() == SkBitmap::kARGB_8888_Config); + DCHECK(bitmap.colorType() == kPMColor_SkColorType); int64_t r_sum = 0; int64_t g_sum = 0; @@ -478,8 +488,8 @@ bool ApplyColorReduction(const SkBitmap& source_bitmap, DCHECK(source_bitmap.getPixels()); DCHECK(target_bitmap->getPixels()); - DCHECK_EQ(SkBitmap::kARGB_8888_Config, source_bitmap.config()); - DCHECK_EQ(SkBitmap::kA8_Config, target_bitmap->config()); + DCHECK_EQ(kPMColor_SkColorType, source_bitmap.colorType()); + DCHECK_EQ(kAlpha_8_SkColorType, target_bitmap->colorType()); DCHECK_EQ(source_bitmap.height(), target_bitmap->height()); DCHECK_EQ(source_bitmap.width(), target_bitmap->width()); DCHECK(!source_bitmap.empty()); diff --git a/chromium/ui/gfx/color_analysis.h b/chromium/ui/gfx/color_analysis.h index dcbfabc9d77..dd273bc11e4 100644 --- a/chromium/ui/gfx/color_analysis.h +++ b/chromium/ui/gfx/color_analysis.h @@ -17,6 +17,8 @@ class SkBitmap; namespace color_utils { +struct HSL; + // This class exposes the sampling method to the caller, which allows // stubbing out for things like unit tests. Might be useful to pass more // arguments into the GetSample method in the future (such as which @@ -52,14 +54,13 @@ class GFX_EXPORT GridSampler : public KMeanImageSampler { GFX_EXPORT SkColor FindClosestColor(const uint8_t* image, int width, int height, SkColor color); -// Returns an SkColor that represents the calculated dominant color in the png. -// This uses a KMean clustering algorithm to find clusters of pixel colors in -// RGB space. -// |png| represents the data of a png encoded image. -// |darkness_limit| represents the minimum sum of the RGB components that is -// acceptable as a color choice. This can be from 0 to 765. -// |brightness_limit| represents the maximum sum of the RGB components that is -// acceptable as a color choice. This can be from 0 to 765. +// Returns an SkColor that represents the calculated dominant color in the +// image. This uses a KMean clustering algorithm to find clusters of pixel +// colors in RGB space. +// |png|/|bitmap| represents the data of a png/bitmap encoded image. +// |lower_bound| represents the minimum bound of HSL values to allow. +// |upper_bound| represents the maximum bound of HSL values to allow. +// See color_utils::IsWithinHSLRange() for description of these bounds. // // RGB KMean Algorithm (N clusters, M iterations): // 1.Pick N starting colors by randomly sampling the pixels. If you see a @@ -82,21 +83,28 @@ GFX_EXPORT SkColor FindClosestColor(const uint8_t* image, int width, int height, // the clusters by weight (where weight is the number of pixels that make up // this cluster). // 6.Going through the sorted list of clusters, pick the first cluster with the -// largest weight that's centroid fulfills the equation -// |darkness_limit| < SUM(R, G, B) < |brightness_limit|. Return that color. +// largest weight that's centroid falls between |lower_bound| and +// |upper_bound|. Return that color. // If no color fulfills that requirement return the color with the largest // weight regardless of whether or not it fulfills the equation above. -// -// Note: Switching to HSV space did not improve the results of this algorithm -// for typical favicon images. +GFX_EXPORT SkColor + CalculateKMeanColorOfPNG(scoped_refptr<base::RefCountedMemory> png, + const HSL& lower_bound, + const HSL& upper_bound, + KMeanImageSampler* sampler); +// Computes a dominant color using the above algorithm and reasonable defaults +// for |lower_bound|, |upper_bound| and |sampler|. GFX_EXPORT SkColor CalculateKMeanColorOfPNG( - scoped_refptr<base::RefCountedMemory> png, - uint32_t darkness_limit, - uint32_t brightness_limit, - KMeanImageSampler* sampler); - -// Computes a dominant color for an SkBitmap using the above algorithm and -// reasonable defaults for |darkness_limit|, |brightness_limit| and |sampler|. + scoped_refptr<base::RefCountedMemory> png); + +// Returns an SkColor that represents the calculated dominant color in the +// image. See CalculateKMeanColorOfPNG() for details. +GFX_EXPORT SkColor CalculateKMeanColorOfBitmap(const SkBitmap& bitmap, + const HSL& lower_bound, + const HSL& upper_bound, + KMeanImageSampler* sampler); +// Computes a dominant color using the above algorithm and reasonable defaults +// for |lower_bound|, |upper_bound| and |sampler|. GFX_EXPORT SkColor CalculateKMeanColorOfBitmap(const SkBitmap& bitmap); // Compute color covariance matrix for the input bitmap. diff --git a/chromium/ui/gfx/color_analysis_unittest.cc b/chromium/ui/gfx/color_analysis_unittest.cc index 7757eb959ba..8e6ec831c1e 100644 --- a/chromium/ui/gfx/color_analysis_unittest.cc +++ b/chromium/ui/gfx/color_analysis_unittest.cc @@ -10,11 +10,11 @@ #include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkColor.h" #include "ui/gfx/canvas.h" +#include "ui/gfx/color_utils.h" +#include "ui/gfx/image/image.h" #include "ui/gfx/rect.h" -using color_utils::FindClosestColor; - -namespace { +namespace color_utils { const unsigned char k1x1White[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, @@ -87,7 +87,24 @@ const unsigned char k1x3BlueRed[] = { 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; -class MockKMeanImageSampler : public color_utils::KMeanImageSampler { +const HSL kDefaultLowerBound = {-1, -1, 0.15}; +const HSL kDefaultUpperBound = {-1, -1, 0.85}; + +// Creates a 1-dimensional png of the pixel colors found in |colors|. +scoped_refptr<base::RefCountedMemory> CreateTestPNG( + const std::vector<SkColor>& colors) { + SkBitmap bitmap; + bitmap.setConfig(SkBitmap::kARGB_8888_Config, colors.size(), 1); + bitmap.allocPixels(); + + SkAutoLockPixels lock(bitmap); + for (size_t i = 0; i < colors.size(); ++i) { + bitmap.eraseArea(SkIRect::MakeXYWH(i, 0, 1, 1), colors[i]); + } + return gfx::Image::CreateFrom1xBitmap(bitmap).As1xPNGBytes(); +} + +class MockKMeanImageSampler : public KMeanImageSampler { public: MockKMeanImageSampler() : current_result_index_(0) { } @@ -104,15 +121,6 @@ class MockKMeanImageSampler : public color_utils::KMeanImageSampler { prebaked_sample_results_.push_back(sample); } - void Reset() { - prebaked_sample_results_.clear(); - ResetCounter(); - } - - void ResetCounter() { - current_result_index_ = 0; - } - virtual int GetSample(int width, int height) OVERRIDE { if (current_result_index_ >= prebaked_sample_results_.size()) { current_result_index_ = 0; @@ -156,8 +164,6 @@ void Calculate8bitBitmapMinMax(const SkBitmap& bitmap, } } -} // namespace - class ColorAnalysisTest : public testing::Test { }; @@ -171,13 +177,13 @@ TEST_F(ColorAnalysisTest, CalculatePNGKMeanAllWhite) { k1x1White, k1x1White + sizeof(k1x1White) / sizeof(unsigned char)))); - SkColor color = - color_utils::CalculateKMeanColorOfPNG(png, 100, 600, &test_sampler); + SkColor color = CalculateKMeanColorOfPNG( + png, kDefaultLowerBound, kDefaultUpperBound, &test_sampler); EXPECT_EQ(color, SK_ColorWHITE); } -TEST_F(ColorAnalysisTest, CalculatePNGKMeanIgnoreWhite) { +TEST_F(ColorAnalysisTest, CalculatePNGKMeanIgnoreWhiteLightness) { MockKMeanImageSampler test_sampler; test_sampler.AddSample(0); test_sampler.AddSample(1); @@ -189,10 +195,10 @@ TEST_F(ColorAnalysisTest, CalculatePNGKMeanIgnoreWhite) { k1x3BlueWhite, k1x3BlueWhite + sizeof(k1x3BlueWhite) / sizeof(unsigned char)))); - SkColor color = - color_utils::CalculateKMeanColorOfPNG(png, 100, 600, &test_sampler); + SkColor color = CalculateKMeanColorOfPNG( + png, kDefaultLowerBound, kDefaultUpperBound, &test_sampler); - EXPECT_EQ(color, SkColorSetARGB(0xFF, 0x00, 0x00, 0xFF)); + EXPECT_EQ(SkColorSetARGB(0xFF, 0x00, 0x00, 0xFF), color); } TEST_F(ColorAnalysisTest, CalculatePNGKMeanPickMostCommon) { @@ -207,14 +213,51 @@ TEST_F(ColorAnalysisTest, CalculatePNGKMeanPickMostCommon) { k1x3BlueRed, k1x3BlueRed + sizeof(k1x3BlueRed) / sizeof(unsigned char)))); - SkColor color = - color_utils::CalculateKMeanColorOfPNG(png, 100, 600, &test_sampler); + SkColor color = CalculateKMeanColorOfPNG( + png, kDefaultLowerBound, kDefaultUpperBound, &test_sampler); - EXPECT_EQ(color, SkColorSetARGB(0xFF, 0xFF, 0x00, 0x00)); + EXPECT_EQ(SkColorSetARGB(0xFF, 0xFF, 0x00, 0x00), color); +} + +TEST_F(ColorAnalysisTest, CalculatePNGKMeanIgnoreRedHue) { + MockKMeanImageSampler test_sampler; + test_sampler.AddSample(0); + test_sampler.AddSample(1); + test_sampler.AddSample(2); + + std::vector<SkColor> colors(4, SK_ColorRED); + colors[1] = SK_ColorBLUE; + + scoped_refptr<base::RefCountedMemory> png = CreateTestPNG(colors); + + HSL lower = {0.2, -1, 0.15}; + HSL upper = {0.8, -1, 0.85}; + SkColor color = CalculateKMeanColorOfPNG( + png, lower, upper, &test_sampler); + + EXPECT_EQ(SK_ColorBLUE, color); +} + +TEST_F(ColorAnalysisTest, CalculatePNGKMeanIgnoreGreySaturation) { + MockKMeanImageSampler test_sampler; + test_sampler.AddSample(0); + test_sampler.AddSample(1); + test_sampler.AddSample(2); + + std::vector<SkColor> colors(4, SK_ColorGRAY); + colors[1] = SK_ColorBLUE; + + scoped_refptr<base::RefCountedMemory> png = CreateTestPNG(colors); + HSL lower = {-1, 0.3, -1}; + HSL upper = {-1, 1, -1}; + SkColor color = CalculateKMeanColorOfPNG( + png, lower, upper, &test_sampler); + + EXPECT_EQ(SK_ColorBLUE, color); } TEST_F(ColorAnalysisTest, GridSampler) { - color_utils::GridSampler sampler; + GridSampler sampler; const int kWidth = 16; const int kHeight = 16; // Sample starts at 1,1. @@ -263,7 +306,7 @@ TEST_F(ColorAnalysisTest, CalculateKMeanColorOfBitmap) { bitmap.allocPixels(); bitmap.eraseARGB(255, 100, 150, 200); - SkColor color = color_utils::CalculateKMeanColorOfBitmap(bitmap); + SkColor color = CalculateKMeanColorOfBitmap(bitmap); EXPECT_EQ(255u, SkColorGetA(color)); // Color values are not exactly equal due to reversal of premultiplied alpha. EXPECT_TRUE(ChannelApproximatelyEqual(100, SkColorGetR(color))); @@ -272,7 +315,7 @@ TEST_F(ColorAnalysisTest, CalculateKMeanColorOfBitmap) { // Test a bitmap with an alpha channel. bitmap.eraseARGB(128, 100, 150, 200); - color = color_utils::CalculateKMeanColorOfBitmap(bitmap); + color = CalculateKMeanColorOfBitmap(bitmap); // Alpha channel should be ignored for dominant color calculation. EXPECT_EQ(255u, SkColorGetA(color)); @@ -285,11 +328,10 @@ TEST_F(ColorAnalysisTest, ComputeColorCovarianceTrivial) { SkBitmap bitmap; bitmap.setConfig(SkBitmap::kARGB_8888_Config, 100, 200); - EXPECT_EQ(gfx::Matrix3F::Zeros(), - color_utils::ComputeColorCovariance(bitmap)); + EXPECT_EQ(gfx::Matrix3F::Zeros(), ComputeColorCovariance(bitmap)); bitmap.allocPixels(); - bitmap.eraseRGB(50, 150, 200); - gfx::Matrix3F covariance = color_utils::ComputeColorCovariance(bitmap); + bitmap.eraseARGB(255, 50, 150, 200); + gfx::Matrix3F covariance = ComputeColorCovariance(bitmap); // The answer should be all zeros. EXPECT_TRUE(covariance == gfx::Matrix3F::Zeros()); } @@ -306,7 +348,7 @@ TEST_F(ColorAnalysisTest, ComputeColorCovarianceWithCanvas) { SkBitmap bitmap = skia::GetTopDevice(*canvas.sk_canvas())->accessBitmap(false); - gfx::Matrix3F covariance = color_utils::ComputeColorCovariance(bitmap); + gfx::Matrix3F covariance = ComputeColorCovariance(bitmap); gfx::Matrix3F expected_covariance = gfx::Matrix3F::Zeros(); expected_covariance.set(2400, 400, -1600, @@ -324,12 +366,11 @@ TEST_F(ColorAnalysisTest, ApplyColorReductionSingleColor) { source.allocPixels(); result.allocPixels(); - source.eraseRGB(50, 150, 200); + source.eraseARGB(255, 50, 150, 200); gfx::Vector3dF transform(1.0f, .5f, 0.1f); // This transform, if not scaled, should result in GL=145. - EXPECT_TRUE(color_utils::ApplyColorReduction( - source, transform, false, &result)); + EXPECT_TRUE(ApplyColorReduction(source, transform, false, &result)); uint8_t min_gl = 0; uint8_t max_gl = 0; @@ -338,24 +379,21 @@ TEST_F(ColorAnalysisTest, ApplyColorReductionSingleColor) { EXPECT_EQ(145, max_gl); // Now scan requesting rescale. Expect all 0. - EXPECT_TRUE(color_utils::ApplyColorReduction( - source, transform, true, &result)); + EXPECT_TRUE(ApplyColorReduction(source, transform, true, &result)); Calculate8bitBitmapMinMax(result, &min_gl, &max_gl); EXPECT_EQ(0, min_gl); EXPECT_EQ(0, max_gl); // Test cliping to upper limit. transform.set_z(1.1f); - EXPECT_TRUE(color_utils::ApplyColorReduction( - source, transform, false, &result)); + EXPECT_TRUE(ApplyColorReduction(source, transform, false, &result)); Calculate8bitBitmapMinMax(result, &min_gl, &max_gl); EXPECT_EQ(0xFF, min_gl); EXPECT_EQ(0xFF, max_gl); // Test cliping to upper limit. transform.Scale(-1.0f); - EXPECT_TRUE(color_utils::ApplyColorReduction( - source, transform, false, &result)); + EXPECT_TRUE(ApplyColorReduction(source, transform, false, &result)); Calculate8bitBitmapMinMax(result, &min_gl, &max_gl); EXPECT_EQ(0x0, min_gl); EXPECT_EQ(0x0, max_gl); @@ -376,8 +414,7 @@ TEST_F(ColorAnalysisTest, ApplyColorReductionBlackAndWhite) { result.allocPixels(); gfx::Vector3dF transform(1.0f, 0.5f, 0.1f); - EXPECT_TRUE(color_utils::ApplyColorReduction( - source, transform, true, &result)); + EXPECT_TRUE(ApplyColorReduction(source, transform, true, &result)); uint8_t min_gl = 0; uint8_t max_gl = 0; Calculate8bitBitmapMinMax(result, &min_gl, &max_gl); @@ -389,8 +426,7 @@ TEST_F(ColorAnalysisTest, ApplyColorReductionBlackAndWhite) { // Reverse test. transform.Scale(-1.0f); - EXPECT_TRUE(color_utils::ApplyColorReduction( - source, transform, true, &result)); + EXPECT_TRUE(ApplyColorReduction(source, transform, true, &result)); min_gl = 0; max_gl = 0; Calculate8bitBitmapMinMax(result, &min_gl, &max_gl); @@ -417,8 +453,7 @@ TEST_F(ColorAnalysisTest, ApplyColorReductionMultiColor) { result.allocPixels(); gfx::Vector3dF transform(1.0f, 0.5f, 0.1f); - EXPECT_TRUE(color_utils::ApplyColorReduction( - source, transform, false, &result)); + EXPECT_TRUE(ApplyColorReduction(source, transform, false, &result)); uint8_t min_gl = 0; uint8_t max_gl = 0; Calculate8bitBitmapMinMax(result, &min_gl, &max_gl); @@ -428,8 +463,7 @@ TEST_F(ColorAnalysisTest, ApplyColorReductionMultiColor) { EXPECT_EQ(max_gl, SkColorGetA(result.getColor(150, 0))); EXPECT_EQ(100U, SkColorGetA(result.getColor(0, 0))); - EXPECT_TRUE(color_utils::ApplyColorReduction( - source, transform, true, &result)); + EXPECT_TRUE(ApplyColorReduction(source, transform, true, &result)); Calculate8bitBitmapMinMax(result, &min_gl, &max_gl); EXPECT_EQ(0, min_gl); EXPECT_EQ(255, max_gl); @@ -445,10 +479,10 @@ TEST_F(ColorAnalysisTest, ComputePrincipalComponentImageNotComputable) { source.allocPixels(); result.allocPixels(); - source.eraseRGB(50, 150, 200); + source.eraseARGB(255, 50, 150, 200); // This computation should fail since all colors always vary together. - EXPECT_FALSE(color_utils::ComputePrincipalComponentImage(source, &result)); + EXPECT_FALSE(ComputePrincipalComponentImage(source, &result)); } TEST_F(ColorAnalysisTest, ComputePrincipalComponentImage) { @@ -465,7 +499,7 @@ TEST_F(ColorAnalysisTest, ComputePrincipalComponentImage) { result.allocPixels(); // This computation should fail since all colors always vary together. - EXPECT_TRUE(color_utils::ComputePrincipalComponentImage(source, &result)); + EXPECT_TRUE(ComputePrincipalComponentImage(source, &result)); uint8_t min_gl = 0; uint8_t max_gl = 0; @@ -477,3 +511,5 @@ TEST_F(ColorAnalysisTest, ComputePrincipalComponentImage) { EXPECT_EQ(max_gl, SkColorGetA(result.getColor(299, 199))); EXPECT_EQ(93U, SkColorGetA(result.getColor(150, 0))); } + +} // namespace color_utils diff --git a/chromium/ui/gfx/color_profile.cc b/chromium/ui/gfx/color_profile.cc index fcd9a52ea0c..bb7c3d546c2 100644 --- a/chromium/ui/gfx/color_profile.cc +++ b/chromium/ui/gfx/color_profile.cc @@ -8,8 +8,15 @@ namespace gfx { #if defined(OS_WIN) || defined(OS_MACOSX) void ReadColorProfile(std::vector<char>* profile); +GFX_EXPORT bool GetDisplayColorProfile(const gfx::Rect& bounds, + std::vector<char>* profile); #else void ReadColorProfile(std::vector<char>* profile) { } +GFX_EXPORT bool GetDisplayColorProfile(const gfx::Rect& bounds, + std::vector<char>* profile) { + // TODO(port): consider screen color profile support. + return false; +} #endif ColorProfile::ColorProfile() { diff --git a/chromium/ui/gfx/color_profile.h b/chromium/ui/gfx/color_profile.h index 20a02bf169f..790bfe57fff 100644 --- a/chromium/ui/gfx/color_profile.h +++ b/chromium/ui/gfx/color_profile.h @@ -7,8 +7,7 @@ #include <vector> -#include "base/basictypes.h" - +#include "ui/gfx/geometry/rect.h" #include "ui/gfx/gfx_export.h" namespace gfx { @@ -31,10 +30,16 @@ class GFX_EXPORT ColorProfile { DISALLOW_COPY_AND_ASSIGN(ColorProfile); }; -// Loads the monitor color space if available. -GFX_EXPORT void GetColorProfile(std::vector<char>* profile); +inline bool InvalidColorProfileLength(size_t length) { + return (length < kMinProfileLength) || (length > kMaxProfileLength); +} +// Return the color profile of the display nearest the screen bounds. On Win32, +// this may read a file from disk, so it shouldn't be run on the UI/IO threads. +// If the given bounds are empty, or are off-screen, return false meaning there +// is no color profile associated with the bounds. +GFX_EXPORT bool GetDisplayColorProfile(const gfx::Rect& bounds, + std::vector<char>* profile); } // namespace gfx #endif // UI_GFX_COLOR_PROFILE_H_ - diff --git a/chromium/ui/gfx/color_profile_mac.cc b/chromium/ui/gfx/color_profile_mac.cc deleted file mode 100644 index 2c7f686a5f4..00000000000 --- a/chromium/ui/gfx/color_profile_mac.cc +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/gfx/color_profile.h" - -#include "base/mac/mac_util.h" - -namespace gfx { - -void ReadColorProfile(std::vector<char>* profile) { - CGColorSpaceRef monitor_color_space(base::mac::GetSystemColorSpace()); - base::ScopedCFTypeRef<CFDataRef> icc_profile( - CGColorSpaceCopyICCProfile(monitor_color_space)); - if (icc_profile) { - size_t length = CFDataGetLength(icc_profile); - if (length > gfx::kMaxProfileLength) - return; - if (length < gfx::kMinProfileLength) - return; - const unsigned char* sys_profile = CFDataGetBytePtr(icc_profile); - profile->assign(sys_profile, sys_profile + length); - } -} - -} // namespace gfx - diff --git a/chromium/ui/gfx/color_profile_mac.mm b/chromium/ui/gfx/color_profile_mac.mm new file mode 100644 index 00000000000..c36072d9330 --- /dev/null +++ b/chromium/ui/gfx/color_profile_mac.mm @@ -0,0 +1,32 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/gfx/color_profile.h" + +#include "base/mac/mac_util.h" + +namespace gfx { + +bool GetDisplayColorProfile(const gfx::Rect& bounds, + std::vector<char>* profile) { + if (bounds.IsEmpty()) + return false; + // TODO(noel): implement. + return false; +} + +void ReadColorProfile(std::vector<char>* profile) { + CGColorSpaceRef monitor_color_space(base::mac::GetSystemColorSpace()); + base::ScopedCFTypeRef<CFDataRef> icc_profile( + CGColorSpaceCopyICCProfile(monitor_color_space)); + if (!icc_profile) + return; + size_t length = CFDataGetLength(icc_profile); + if (gfx::InvalidColorProfileLength(length)) + return; + const unsigned char* data = CFDataGetBytePtr(icc_profile); + profile->assign(data, data + length); +} + +} // namespace gfx diff --git a/chromium/ui/gfx/color_profile_win.cc b/chromium/ui/gfx/color_profile_win.cc index 2e08a36d4b6..a22c3d96a18 100644 --- a/chromium/ui/gfx/color_profile_win.cc +++ b/chromium/ui/gfx/color_profile_win.cc @@ -4,32 +4,118 @@ #include "ui/gfx/color_profile.h" +#include <map> #include <windows.h> #include "base/file_util.h" +#include "base/lazy_instance.h" +#include "base/synchronization/lock.h" namespace gfx { +class ColorProfileCache { + public: + // A thread-safe cache of color profiles keyed by windows device name. + ColorProfileCache() {} + + bool Find(const std::wstring& device, std::vector<char>* profile) { + base::AutoLock lock(lock_); + DeviceColorProfile::const_iterator it = cache_.find(device); + if (it == cache_.end()) + return false; + *profile = it->second; + return true; + } + + void Insert(const std::wstring& device, const std::vector<char>& profile) { + base::AutoLock lock(lock_); + cache_[device] = profile; + } + + bool Erase(const std::wstring& device) { + base::AutoLock lock(lock_); + DeviceColorProfile::iterator it = cache_.find(device); + if (it == cache_.end()) + return false; + cache_.erase(device); + return true; + } + + void Clear() { + base::AutoLock lock(lock_); + cache_.clear(); + } + + private: + typedef std::map<std::wstring, std::vector<char> > DeviceColorProfile; + + DeviceColorProfile cache_; + base::Lock lock_; + + DISALLOW_COPY_AND_ASSIGN(ColorProfileCache); +}; + +base::LazyInstance<ColorProfileCache>::Leaky g_color_profile_cache = + LAZY_INSTANCE_INITIALIZER; + +inline ColorProfileCache& GetColorProfileCache() { + return g_color_profile_cache.Get(); +} + +bool GetDisplayColorProfile(const gfx::Rect& bounds, + std::vector<char>* profile) { + DCHECK(profile->empty()); + + RECT rect = bounds.ToRECT(); + HMONITOR handle = ::MonitorFromRect(&rect, MONITOR_DEFAULTTONULL); + if (bounds.IsEmpty() || !handle) + return false; + + MONITORINFOEX monitor; + monitor.cbSize = sizeof(MONITORINFOEX); + CHECK(::GetMonitorInfo(handle, &monitor)); + if (GetColorProfileCache().Find(monitor.szDevice, profile)) + return true; + + HDC hdc = ::CreateDC(monitor.szDevice, NULL, NULL, NULL); + DWORD path_length = MAX_PATH; + WCHAR path[MAX_PATH + 1]; + BOOL result = ::GetICMProfile(hdc, &path_length, path); + ::DeleteDC(hdc); + if (!result) + return false; + + base::FilePath file_name = base::FilePath(path).BaseName(); + if (file_name != base::FilePath(L"sRGB Color Space Profile.icm")) { + std::string data; + if (base::ReadFileToString(base::FilePath(path), &data)) + profile->assign(data.data(), data.data() + data.size()); + size_t length = profile->size(); + if (gfx::InvalidColorProfileLength(length)) + profile->clear(); + } + + GetColorProfileCache().Insert(monitor.szDevice, *profile); + return true; +} + void ReadColorProfile(std::vector<char>* profile) { // TODO: support multiple monitors. HDC screen_dc = GetDC(NULL); DWORD path_len = MAX_PATH; WCHAR path[MAX_PATH + 1]; - BOOL res = GetICMProfile(screen_dc, &path_len, path); + BOOL result = GetICMProfile(screen_dc, &path_len, path); ReleaseDC(NULL, screen_dc); - if (!res) + if (!result) return; std::string profileData; if (!base::ReadFileToString(base::FilePath(path), &profileData)) return; size_t length = profileData.size(); - if (length > gfx::kMaxProfileLength) - return; - if (length < gfx::kMinProfileLength) + if (gfx::InvalidColorProfileLength(length)) return; profile->assign(profileData.data(), profileData.data() + length); } } // namespace gfx - diff --git a/chromium/ui/gfx/color_profile_win_unittest.cc b/chromium/ui/gfx/color_profile_win_unittest.cc new file mode 100644 index 00000000000..604ee36201b --- /dev/null +++ b/chromium/ui/gfx/color_profile_win_unittest.cc @@ -0,0 +1,32 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/gfx/color_profile.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +bool TestColorProfileUsingScreenBounds(const gfx::Rect& bounds) { + std::vector<char> color_profile; + return gfx::GetDisplayColorProfile(bounds, &color_profile); +} + +TEST(ColorProfileTest, GetDisplayColorProfile) { + const gfx::Rect in_screen_bounds(10, 10, 100, 100); + EXPECT_TRUE(TestColorProfileUsingScreenBounds(in_screen_bounds)); +} + +TEST(ColorProfileTest, GetDisplayColorProfileForOffScreenBounds) { + const gfx::Rect off_screen_bounds(-100, -100, 10, 10); + EXPECT_FALSE(TestColorProfileUsingScreenBounds(off_screen_bounds)); +} + +TEST(ColorProfileTest, GetDisplayColorProfileForEmptyBounds) { + const gfx::Rect empty_screen_bounds(10, 10, 0, 0); + EXPECT_TRUE(empty_screen_bounds.IsEmpty()); + EXPECT_FALSE(TestColorProfileUsingScreenBounds(empty_screen_bounds)); +} + +} // namespace diff --git a/chromium/ui/gfx/color_utils.cc b/chromium/ui/gfx/color_utils.cc index 9ced80f3952..3e00326f522 100644 --- a/chromium/ui/gfx/color_utils.cc +++ b/chromium/ui/gfx/color_utils.cc @@ -151,6 +151,36 @@ SkColor HSLToSkColor(const HSL& hsl, SkAlpha alpha) { calcHue(temp1, temp2, hue - 1.0 / 3.0)); } +bool IsWithinHSLRange(const HSL& hsl, + const HSL& lower_bound, + const HSL& upper_bound) { + DCHECK(hsl.h >= 0 && hsl.h <= 1) << hsl.h; + DCHECK(hsl.s >= 0 && hsl.s <= 1) << hsl.s; + DCHECK(hsl.l >= 0 && hsl.l <= 1) << hsl.l; + DCHECK(lower_bound.h < 0 || upper_bound.h < 0 || + (lower_bound.h <= 1 && upper_bound.h <= lower_bound.h + 1)) + << "lower_bound.h: " << lower_bound.h + << ", upper_bound.h: " << upper_bound.h; + DCHECK(lower_bound.s < 0 || upper_bound.s < 0 || + (lower_bound.s <= upper_bound.s && upper_bound.s <= 1)) + << "lower_bound.s: " << lower_bound.s + << ", upper_bound.s: " << upper_bound.s; + DCHECK(lower_bound.l < 0 || upper_bound.l < 0 || + (lower_bound.l <= upper_bound.l && upper_bound.l <= 1)) + << "lower_bound.l: " << lower_bound.l + << ", upper_bound.l: " << upper_bound.l; + + // If the upper hue is >1, the given hue bounds wrap around at 1. + bool matches_hue = upper_bound.h > 1 + ? hsl.h >= lower_bound.h || hsl.h <= upper_bound.h - 1 + : hsl.h >= lower_bound.h && hsl.h <= upper_bound.h; + return (upper_bound.h < 0 || lower_bound.h < 0 || matches_hue) && + (upper_bound.s < 0 || lower_bound.s < 0 || + (hsl.s >= lower_bound.s && hsl.s <= upper_bound.s)) && + (upper_bound.l < 0 || lower_bound.l < 0 || + (hsl.l >= lower_bound.l && hsl.l <= upper_bound.l)); +} + SkColor HSLShift(SkColor color, const HSL& shift) { HSL hsl; int alpha = SkColorGetA(color); @@ -194,7 +224,7 @@ SkColor HSLShift(SkColor color, const HSL& shift) { } void BuildLumaHistogram(const SkBitmap& bitmap, int histogram[256]) { - DCHECK_EQ(SkBitmap::kARGB_8888_Config, bitmap.config()); + DCHECK_EQ(kPMColor_SkColorType, bitmap.colorType()); SkAutoLockPixels bitmap_lock(bitmap); diff --git a/chromium/ui/gfx/color_utils.h b/chromium/ui/gfx/color_utils.h index 6dd6300aa3b..9446654cdbe 100644 --- a/chromium/ui/gfx/color_utils.h +++ b/chromium/ui/gfx/color_utils.h @@ -29,6 +29,20 @@ GFX_EXPORT double RelativeLuminance(SkColor color); GFX_EXPORT void SkColorToHSL(SkColor c, HSL* hsl); GFX_EXPORT SkColor HSLToSkColor(const HSL& hsl, SkAlpha alpha); +// Determines whether the given |hsl| falls within the given range for each +// component. All components of |hsl| are expected to be in the range [0, 1]. +// +// If a component is negative in either |lower_bound| or |upper_bound|, that +// component will be ignored. +// +// For hue, the lower bound should be in the range [0, 1] and the upper bound +// should be in the range [(lower bound), (lower bound + 1)]. +// For saturation and value, bounds should be specified in the range [0, 1], +// with the lower bound less than the upper bound. +GFX_EXPORT bool IsWithinHSLRange(const HSL& hsl, + const HSL& lower_bound, + const HSL& upper_bound); + // HSL-Shift an SkColor. The shift values are in the range of 0-1, with the // option to specify -1 for 'no change'. The shift values are defined as: // hsl_shift[0] (hue): The absolute hue value - 0 and 1 map diff --git a/chromium/ui/gfx/color_utils_unittest.cc b/chromium/ui/gfx/color_utils_unittest.cc index 59eaeba1e3e..b90ae8e2c42 100644 --- a/chromium/ui/gfx/color_utils_unittest.cc +++ b/chromium/ui/gfx/color_utils_unittest.cc @@ -9,17 +9,19 @@ #include "third_party/skia/include/core/SkColorPriv.h" #include "ui/gfx/color_utils.h" +namespace color_utils { + TEST(ColorUtils, SkColorToHSLRed) { - color_utils::HSL hsl = { 0, 0, 0 }; - color_utils::SkColorToHSL(SK_ColorRED, &hsl); + HSL hsl = {0, 0, 0}; + SkColorToHSL(SK_ColorRED, &hsl); EXPECT_DOUBLE_EQ(hsl.h, 0); EXPECT_DOUBLE_EQ(hsl.s, 1); EXPECT_DOUBLE_EQ(hsl.l, 0.5); } TEST(ColorUtils, SkColorToHSLGrey) { - color_utils::HSL hsl = { 0, 0, 0 }; - color_utils::SkColorToHSL(SkColorSetARGB(255, 128, 128, 128), &hsl); + HSL hsl = {0, 0, 0}; + SkColorToHSL(SkColorSetARGB(255, 128, 128, 128), &hsl); EXPECT_DOUBLE_EQ(hsl.h, 0); EXPECT_DOUBLE_EQ(hsl.s, 0); EXPECT_EQ(static_cast<int>(hsl.l * 100), @@ -28,24 +30,23 @@ TEST(ColorUtils, SkColorToHSLGrey) { TEST(ColorUtils, HSLToSkColorWithAlpha) { SkColor red = SkColorSetARGB(128, 255, 0, 0); - color_utils::HSL hsl = { 0, 1, 0.5 }; - SkColor result = color_utils::HSLToSkColor(hsl, 128); + HSL hsl = {0, 1, 0.5}; + SkColor result = HSLToSkColor(hsl, 128); EXPECT_EQ(SkColorGetA(red), SkColorGetA(result)); EXPECT_EQ(SkColorGetR(red), SkColorGetR(result)); EXPECT_EQ(SkColorGetG(red), SkColorGetG(result)); EXPECT_EQ(SkColorGetB(red), SkColorGetB(result)); } - TEST(ColorUtils, RGBtoHSLRoundTrip) { // Just spot check values near the edges. for (int r = 0; r < 10; ++r) { for (int g = 0; g < 10; ++g) { for (int b = 0; b < 10; ++b) { SkColor rgb = SkColorSetARGB(255, r, g, b); - color_utils::HSL hsl = { 0, 0, 0 }; - color_utils::SkColorToHSL(rgb, &hsl); - SkColor out = color_utils::HSLToSkColor(hsl, 255); + HSL hsl = {0, 0, 0}; + SkColorToHSL(rgb, &hsl); + SkColor out = HSLToSkColor(hsl, 255); EXPECT_EQ(SkColorGetR(out), SkColorGetR(rgb)); EXPECT_EQ(SkColorGetG(out), SkColorGetG(rgb)); EXPECT_EQ(SkColorGetB(out), SkColorGetB(rgb)); @@ -56,9 +57,9 @@ TEST(ColorUtils, RGBtoHSLRoundTrip) { for (int g = 240; g < 256; ++g) { for (int b = 240; b < 256; ++b) { SkColor rgb = SkColorSetARGB(255, r, g, b); - color_utils::HSL hsl = { 0, 0, 0 }; - color_utils::SkColorToHSL(rgb, &hsl); - SkColor out = color_utils::HSLToSkColor(hsl, 255); + HSL hsl = {0, 0, 0}; + SkColorToHSL(rgb, &hsl); + SkColor out = HSLToSkColor(hsl, 255); EXPECT_EQ(SkColorGetR(out), SkColorGetR(rgb)); EXPECT_EQ(SkColorGetG(out), SkColorGetG(rgb)); EXPECT_EQ(SkColorGetB(out), SkColorGetB(rgb)); @@ -67,12 +68,44 @@ TEST(ColorUtils, RGBtoHSLRoundTrip) { } } +TEST(ColorUtils, IsWithinHSLRange) { + HSL hsl = {0.3, 0.4, 0.5}; + HSL lower = {0.2, 0.3, 0.4}; + HSL upper = {0.4, 0.5, 0.6}; + EXPECT_TRUE(IsWithinHSLRange(hsl, lower, upper)); + // Bounds are inclusive. + hsl.h = 0.2; + EXPECT_TRUE(IsWithinHSLRange(hsl, lower, upper)); + hsl.h = 0.4; + EXPECT_TRUE(IsWithinHSLRange(hsl, lower, upper)); + hsl.s = 0.3; + EXPECT_TRUE(IsWithinHSLRange(hsl, lower, upper)); + hsl.s = 0.5; + EXPECT_TRUE(IsWithinHSLRange(hsl, lower, upper)); + hsl.l = 0.4; + EXPECT_TRUE(IsWithinHSLRange(hsl, lower, upper)); + hsl.l = 0.6; + EXPECT_TRUE(IsWithinHSLRange(hsl, lower, upper)); +} + +TEST(ColorUtils, IsWithinHSLRangeHueWrapAround) { + HSL hsl = {0.3, 0.4, 0.5}; + HSL lower = {0.8, -1, -1}; + HSL upper = {1.2, -1, -1}; + hsl.h = 0.1; + EXPECT_TRUE(IsWithinHSLRange(hsl, lower, upper)); + hsl.h = 0.9; + EXPECT_TRUE(IsWithinHSLRange(hsl, lower, upper)); + hsl.h = 0.3; + EXPECT_FALSE(IsWithinHSLRange(hsl, lower, upper)); +} + TEST(ColorUtils, ColorToHSLRegisterSpill) { // In a opt build on Linux, this was causing a register spill on my laptop // (Pentium M) when converting from SkColor to HSL. SkColor input = SkColorSetARGB(255, 206, 154, 89); - color_utils::HSL hsl = { -1, -1, -1 }; - SkColor result = color_utils::HSLShift(input, hsl); + HSL hsl = {-1, -1, -1}; + SkColor result = HSLShift(input, hsl); // |result| should be the same as |input| since we passed in a value meaning // no color shift. EXPECT_EQ(SkColorGetA(input), SkColorGetA(result)); @@ -85,16 +118,16 @@ TEST(ColorUtils, AlphaBlend) { SkColor fore = SkColorSetARGB(255, 200, 200, 200); SkColor back = SkColorSetARGB(255, 100, 100, 100); - EXPECT_TRUE(color_utils::AlphaBlend(fore, back, 255) == - fore); - EXPECT_TRUE(color_utils::AlphaBlend(fore, back, 0) == - back); + EXPECT_TRUE(AlphaBlend(fore, back, 255) == fore); + EXPECT_TRUE(AlphaBlend(fore, back, 0) == back); // One is fully transparent, result is partially transparent. back = SkColorSetA(back, 0); - EXPECT_EQ(136U, SkColorGetA(color_utils::AlphaBlend(fore, back, 136))); + EXPECT_EQ(136U, SkColorGetA(AlphaBlend(fore, back, 136))); // Both are fully transparent, result is fully transparent. fore = SkColorSetA(fore, 0); - EXPECT_EQ(0U, SkColorGetA(color_utils::AlphaBlend(fore, back, 255))); + EXPECT_EQ(0U, SkColorGetA(AlphaBlend(fore, back, 255))); } + +} // namespace color_utils diff --git a/chromium/ui/gfx/display.cc b/chromium/ui/gfx/display.cc index 024b1f9aaa6..029359c0938 100644 --- a/chromium/ui/gfx/display.cc +++ b/chromium/ui/gfx/display.cc @@ -35,12 +35,11 @@ float GetForcedDeviceScaleFactorImpl() { return static_cast<float>(scale_in_double); } -const int64 kInvalidDisplayIDForCompileTimeInit = -1; -int64 internal_display_id_ = kInvalidDisplayIDForCompileTimeInit; +int64 internal_display_id_ = -1; } // namespace -const int64 Display::kInvalidDisplayID = kInvalidDisplayIDForCompileTimeInit; +const int64 Display::kInvalidDisplayID = -1; // static float Display::GetForcedDeviceScaleFactor() { @@ -51,7 +50,9 @@ float Display::GetForcedDeviceScaleFactor() { //static bool Display::HasForceDeviceScaleFactor() { - return HasForceDeviceScaleFactorImpl(); + static const bool kHasForceDeviceScaleFactor = + HasForceDeviceScaleFactorImpl(); + return kHasForceDeviceScaleFactor; } Display::Display() @@ -83,6 +84,42 @@ Display::Display(int64 id, const gfx::Rect& bounds) Display::~Display() { } +int Display::RotationAsDegree() const { + switch (rotation_) { + case ROTATE_0: + return 0; + case ROTATE_90: + return 90; + case ROTATE_180: + return 180; + case ROTATE_270: + return 270; + } + + NOTREACHED(); + return 0; +} + +void Display::SetRotationAsDegree(int rotation) { + switch (rotation) { + case 0: + rotation_ = ROTATE_0; + break; + case 90: + rotation_ = ROTATE_90; + break; + case 180: + rotation_ = ROTATE_180; + break; + case 270: + rotation_ = ROTATE_270; + break; + default: + // We should not reach that but we will just ignore the call if we do. + NOTREACHED(); + } +} + Insets Display::GetWorkAreaInsets() const { return gfx::Insets(work_area_.y() - bounds_.y(), work_area_.x() - bounds_.x(), diff --git a/chromium/ui/gfx/display.h b/chromium/ui/gfx/display.h index eb35a5f5c6f..90fc42f5c31 100644 --- a/chromium/ui/gfx/display.h +++ b/chromium/ui/gfx/display.h @@ -73,6 +73,8 @@ class GFX_EXPORT Display { Rotation rotation() const { return rotation_; } void set_rotation(Rotation rotation) { rotation_ = rotation; } + int RotationAsDegree() const; + void SetRotationAsDegree(int rotation); TouchSupport touch_support() const { return touch_support_; } void set_touch_support(TouchSupport support) { touch_support_ = support; } diff --git a/chromium/ui/gfx/display_observer.h b/chromium/ui/gfx/display_observer.h index ca97247f9ed..0730f266206 100644 --- a/chromium/ui/gfx/display_observer.h +++ b/chromium/ui/gfx/display_observer.h @@ -5,6 +5,8 @@ #ifndef UI_GFX_DISPLAY_OBSERVER_H_ #define UI_GFX_DISPLAY_OBSERVER_H_ +#include <stdint.h> + #include "ui/gfx/gfx_export.h" namespace gfx { @@ -15,8 +17,13 @@ class Display; // |DisplaySettingsProvier|. crbug.com/122863. class GFX_EXPORT DisplayObserver { public: - // Called when the |display|'s bound has changed. - virtual void OnDisplayBoundsChanged(const Display& display) = 0; + enum DisplayMetric { + DISPLAY_METRIC_NONE = 0, + DISPLAY_METRIC_BOUNDS = 1 << 0, + DISPLAY_METRIC_WORK_AREA = 1 << 1, + DISPLAY_METRIC_DEVICE_SCALE_FACTOR = 1 << 2, + DISPLAY_METRIC_ROTATION = 1 << 3, + }; // Called when |new_display| has been added. virtual void OnDisplayAdded(const Display& new_display) = 0; @@ -24,6 +31,11 @@ class GFX_EXPORT DisplayObserver { // Called when |old_display| has been removed. virtual void OnDisplayRemoved(const Display& old_display) = 0; + // Called when a |display| has one or more metrics changed. |changed_metrics| + // will contain the information about the change, see |DisplayMetric|. + virtual void OnDisplayMetricsChanged(const Display& display, + uint32_t changed_metrics) = 0; + protected: virtual ~DisplayObserver(); }; diff --git a/chromium/ui/gfx/font.cc b/chromium/ui/gfx/font.cc index 9eea73b608a..31f27b4f294 100644 --- a/chromium/ui/gfx/font.cc +++ b/chromium/ui/gfx/font.cc @@ -18,7 +18,7 @@ Font::Font() : platform_font_(PlatformFont::CreateDefault()) { Font::Font(const Font& other) : platform_font_(other.platform_font_) { } -gfx::Font& Font::operator=(const Font& other) { +Font& Font::operator=(const Font& other) { platform_font_ = other.platform_font_; return *this; } @@ -38,11 +38,7 @@ Font::Font(const std::string& font_name, int font_size) Font::~Font() { } -Font Font::DeriveFont(int size_delta) const { - return DeriveFont(size_delta, GetStyle()); -} - -Font Font::DeriveFont(int size_delta, int style) const { +Font Font::Derive(int size_delta, int style) const { return platform_font_->DeriveFont(size_delta, style); } @@ -58,14 +54,6 @@ int Font::GetCapHeight() const { return platform_font_->GetCapHeight(); } -int Font::GetAverageCharacterWidth() const { - return platform_font_->GetAverageCharacterWidth(); -} - -int Font::GetStringWidth(const base::string16& text) const { - return platform_font_->GetStringWidth(text); -} - int Font::GetExpectedTextWidth(int length) const { return platform_font_->GetExpectedTextWidth(length); } diff --git a/chromium/ui/gfx/font.h b/chromium/ui/gfx/font.h index 963dde70ffe..5ab0f170bda 100644 --- a/chromium/ui/gfx/font.h +++ b/chromium/ui/gfx/font.h @@ -42,7 +42,7 @@ class GFX_EXPORT Font { // Creates a font that is a clone of another font object. Font(const Font& other); - gfx::Font& operator=(const Font& other); + Font& operator=(const Font& other); // Creates a font from the specified native font. explicit Font(NativeFont native_font); @@ -57,16 +57,11 @@ class GFX_EXPORT Font { ~Font(); // Returns a new Font derived from the existing font. - // |size_deta| is the size in pixels to add to the current font. For example, + // |size_delta| is the size in pixels to add to the current font. For example, // a value of 5 results in a font 5 pixels bigger than this font. - Font DeriveFont(int size_delta) const; - - // Returns a new Font derived from the existing font. - // |size_delta| is the size in pixels to add to the current font. See the - // single argument version of this method for an example. // The style parameter specifies the new style for the font, and is a // bitmask of the values: BOLD, ITALIC and UNDERLINE. - Font DeriveFont(int size_delta, int style) const; + Font Derive(int size_delta, int style) const; // Returns the number of vertical pixels needed to display characters from // the specified font. This may include some leading, i.e. height may be @@ -81,15 +76,8 @@ class GFX_EXPORT Font { // Returns the cap height of the font. int GetCapHeight() const; - // Returns the average character width for the font. - int GetAverageCharacterWidth() const; - - // Returns the number of horizontal pixels needed to display the specified - // string. - int GetStringWidth(const base::string16& text) const; - // Returns the expected number of horizontal pixels needed to display the - // specified length of characters. Call GetStringWidth() to retrieve the + // specified length of characters. Call gfx::GetStringWidth() to retrieve the // actual number. int GetExpectedTextWidth(int length) const; diff --git a/chromium/ui/gfx/font_fallback_win.cc b/chromium/ui/gfx/font_fallback_win.cc index 40666ee239a..4426718959b 100644 --- a/chromium/ui/gfx/font_fallback_win.cc +++ b/chromium/ui/gfx/font_fallback_win.cc @@ -24,8 +24,9 @@ void QueryFontsFromRegistry(std::map<std::string, std::string>* map) { base::win::RegistryValueIterator it(HKEY_LOCAL_MACHINE, kFonts); for (; it.Valid(); ++it) { - const std::string filename = StringToLowerASCII(WideToUTF8(it.Value())); - (*map)[filename] = WideToUTF8(it.Name()); + const std::string filename = + StringToLowerASCII(base::WideToUTF8(it.Value())); + (*map)[filename] = base::WideToUTF8(it.Name()); } } @@ -69,7 +70,7 @@ void QueryLinkedFontsFromRegistry(const Font& font, if (FAILED(key.Open(HKEY_LOCAL_MACHINE, kSystemLink, KEY_READ))) return; - const std::wstring original_font_name = UTF8ToWide(font.GetFontName()); + const std::wstring original_font_name = base::UTF8ToWide(font.GetFontName()); std::vector<std::wstring> values; if (FAILED(key.ReadValues(original_font_name.c_str(), &values))) { key.Close(); @@ -79,7 +80,8 @@ void QueryLinkedFontsFromRegistry(const Font& font, std::string filename; std::string font_name; for (size_t i = 0; i < values.size(); ++i) { - internal::ParseFontLinkEntry(WideToUTF8(values[i]), &filename, &font_name); + internal::ParseFontLinkEntry( + base::WideToUTF8(values[i]), &filename, &font_name); // If the font name is present, add that directly, otherwise add the // font names corresponding to the filename. if (!font_name.empty()) { @@ -178,7 +180,8 @@ void ParseFontFamilyString(const std::string& family, const size_t index = font_names->back().find('('); if (index != std::string::npos) { font_names->back().resize(index); - TrimWhitespace(font_names->back(), TRIM_TRAILING, &font_names->back()); + base::TrimWhitespace(font_names->back(), base::TRIM_TRAILING, + &font_names->back()); } } } diff --git a/chromium/ui/gfx/font_list.cc b/chromium/ui/gfx/font_list.cc index 3b1ab9bcd1f..920204bf7a7 100644 --- a/chromium/ui/gfx/font_list.cc +++ b/chromium/ui/gfx/font_list.cc @@ -4,13 +4,9 @@ #include "ui/gfx/font_list.h" -#include <algorithm> - #include "base/lazy_instance.h" -#include "base/logging.h" -#include "base/strings/string_number_conversions.h" -#include "base/strings/string_split.h" #include "base/strings/string_util.h" +#include "ui/gfx/font_list_impl.h" namespace { @@ -18,128 +14,37 @@ namespace { base::LazyInstance<std::string>::Leaky g_default_font_description = LAZY_INSTANCE_INITIALIZER; -// Parses font description into |font_names|, |font_style| and |font_size|. -void ParseFontDescriptionString(const std::string& font_description_string, - std::vector<std::string>* font_names, - int* font_style, - int* font_size) { - base::SplitString(font_description_string, ',', font_names); - DCHECK_GT(font_names->size(), 1U); - - // The last item is [STYLE_OPTIONS] SIZE. - std::vector<std::string> styles_size; - base::SplitString(font_names->back(), ' ', &styles_size); - DCHECK(!styles_size.empty()); - base::StringToInt(styles_size.back(), font_size); - DCHECK_GT(*font_size, 0); - font_names->pop_back(); - - // Font supports BOLD and ITALIC; underline is supported via RenderText. - *font_style = 0; - for (size_t i = 0; i < styles_size.size() - 1; ++i) { - // Styles are separated by white spaces. base::SplitString splits styles - // by space, and it inserts empty string for continuous spaces. - if (styles_size[i].empty()) - continue; - if (!styles_size[i].compare("Bold")) - *font_style |= gfx::Font::BOLD; - else if (!styles_size[i].compare("Italic")) - *font_style |= gfx::Font::ITALIC; - else - NOTREACHED(); - } -} - -// Returns the font style and size as a string. -std::string FontStyleAndSizeToString(int font_style, int font_size) { - std::string result; - if (font_style & gfx::Font::BOLD) - result += "Bold "; - if (font_style & gfx::Font::ITALIC) - result += "Italic "; - result += base::IntToString(font_size); - result += "px"; - return result; -} - -// Returns font description from |font_names|, |font_style|, and |font_size|. -std::string BuildFontDescription(const std::vector<std::string>& font_names, - int font_style, - int font_size) { - std::string description = JoinString(font_names, ','); - description += "," + FontStyleAndSizeToString(font_style, font_size); - return description; -} +// The default instance of gfx::FontListImpl. +base::LazyInstance<scoped_refptr<gfx::FontListImpl> >::Leaky g_default_impl = + LAZY_INSTANCE_INITIALIZER; +bool g_default_impl_initialized = false; } // namespace namespace gfx { -FontList::FontList() - : common_height_(-1), - common_baseline_(-1), - font_style_(-1), - font_size_(-1) { - // SetDefaultFontDescription() must be called and the default font description - // must be set earlier than any call of the default constructor. - DCHECK(!(g_default_font_description == NULL)) // != is not overloaded. - << "SetDefaultFontDescription has not been called."; +FontList::FontList() : impl_(GetDefaultImpl()) {} - font_description_string_ = g_default_font_description.Get(); - if (font_description_string_.empty()) - fonts_.push_back(Font()); -} +FontList::FontList(const FontList& other) : impl_(other.impl_) {} FontList::FontList(const std::string& font_description_string) - : font_description_string_(font_description_string), - common_height_(-1), - common_baseline_(-1), - font_style_(-1), - font_size_(-1) { - DCHECK(!font_description_string.empty()); - // DCHECK description string ends with "px" for size in pixel. - DCHECK(EndsWith(font_description_string, "px", true)); -} + : impl_(new FontListImpl(font_description_string)) {} FontList::FontList(const std::vector<std::string>& font_names, int font_style, int font_size) - : font_description_string_(BuildFontDescription(font_names, font_style, - font_size)), - common_height_(-1), - common_baseline_(-1), - font_style_(font_style), - font_size_(font_size) { - DCHECK(!font_names.empty()); - DCHECK(!font_names[0].empty()); -} + : impl_(new FontListImpl(font_names, font_style, font_size)) {} FontList::FontList(const std::vector<Font>& fonts) - : fonts_(fonts), - common_height_(-1), - common_baseline_(-1), - font_style_(-1), - font_size_(-1) { - DCHECK(!fonts.empty()); - font_style_ = fonts[0].GetStyle(); - font_size_ = fonts[0].GetFontSize(); - if (DCHECK_IS_ON()) { - for (size_t i = 1; i < fonts.size(); ++i) { - DCHECK_EQ(fonts[i].GetStyle(), font_style_); - DCHECK_EQ(fonts[i].GetFontSize(), font_size_); - } - } -} + : impl_(new FontListImpl(fonts)) {} -FontList::FontList(const Font& font) - : common_height_(-1), - common_baseline_(-1), - font_style_(-1), - font_size_(-1) { - fonts_.push_back(font); -} +FontList::FontList(const Font& font) : impl_(new FontListImpl(font)) {} -FontList::~FontList() { +FontList::~FontList() {} + +FontList& FontList::operator=(const FontList& other) { + impl_ = other.impl_; + return *this; } // static @@ -150,151 +55,75 @@ void FontList::SetDefaultFontDescription(const std::string& font_description) { EndsWith(font_description, "px", true)); g_default_font_description.Get() = font_description; + g_default_impl_initialized = false; } -FontList FontList::DeriveFontList(int font_style) const { - return DeriveFontListWithSizeDeltaAndStyle(0, font_style); -} - -FontList FontList::DeriveFontListWithSize(int size) const { - DCHECK_GT(size, 0); - return DeriveFontListWithSizeDeltaAndStyle(size - GetFontSize(), - GetFontStyle()); +FontList FontList::Derive(int size_delta, int font_style) const { + return FontList(impl_->Derive(size_delta, font_style)); } -FontList FontList::DeriveFontListWithSizeDelta(int size_delta) const { - return DeriveFontListWithSizeDeltaAndStyle(size_delta, GetFontStyle()); +FontList FontList::DeriveWithSizeDelta(int size_delta) const { + return Derive(size_delta, GetFontStyle()); } -FontList FontList::DeriveFontListWithSizeDeltaAndStyle(int size_delta, - int style) const { - // If there is a font vector, derive from that. - if (!fonts_.empty()) { - std::vector<Font> fonts = fonts_; - for (size_t i = 0; i < fonts.size(); ++i) - fonts[i] = fonts[i].DeriveFont(size_delta, style); - return FontList(fonts); - } - - // Otherwise, parse the font description string to derive from it. - std::vector<std::string> font_names; - int old_size; - int old_style; - ParseFontDescriptionString(font_description_string_, &font_names, - &old_style, &old_size); - int size = old_size + size_delta; - DCHECK_GT(size, 0); - return FontList(font_names, style, size); +FontList FontList::DeriveWithStyle(int font_style) const { + return Derive(0, font_style); } int FontList::GetHeight() const { - if (common_height_ == -1) - CacheCommonFontHeightAndBaseline(); - return common_height_; + return impl_->GetHeight(); } int FontList::GetBaseline() const { - if (common_baseline_ == -1) - CacheCommonFontHeightAndBaseline(); - return common_baseline_; + return impl_->GetBaseline(); } int FontList::GetCapHeight() const { - // Assume the primary font is used to render Latin characters. - return GetPrimaryFont().GetCapHeight(); -} - -int FontList::GetStringWidth(const base::string16& text) const { - // Rely on the primary font metrics for the time being. - // TODO(yukishiino): Not only the first font, all the fonts in the list should - // be taken into account to compute the pixels needed to display |text|. - // Also this method, including one in Font class, should be deprecated and - // client code should call Canvas::GetStringWidth(text, font_list) directly. - // Our plan is as follows: - // 1. Introduce the FontList version of Canvas::GetStringWidth(). - // 2. Make client code call Canvas::GetStringWidth(). - // 3. Retire {Font,FontList}::GetStringWidth(). - return GetPrimaryFont().GetStringWidth(text); + return impl_->GetCapHeight(); } int FontList::GetExpectedTextWidth(int length) const { - // Rely on the primary font metrics for the time being. - return GetPrimaryFont().GetExpectedTextWidth(length); + return impl_->GetExpectedTextWidth(length); } int FontList::GetFontStyle() const { - if (font_style_ == -1) - CacheFontStyleAndSize(); - return font_style_; + return impl_->GetFontStyle(); } const std::string& FontList::GetFontDescriptionString() const { - if (font_description_string_.empty()) { - DCHECK(!fonts_.empty()); - for (size_t i = 0; i < fonts_.size(); ++i) { - std::string name = fonts_[i].GetFontName(); - font_description_string_ += name; - font_description_string_ += ','; - } - // All fonts have the same style and size. - font_description_string_ += - FontStyleAndSizeToString(fonts_[0].GetStyle(), fonts_[0].GetFontSize()); - } - return font_description_string_; + return impl_->GetFontDescriptionString(); } int FontList::GetFontSize() const { - if (font_size_ == -1) - CacheFontStyleAndSize(); - return font_size_; + return impl_->GetFontSize(); } const std::vector<Font>& FontList::GetFonts() const { - if (fonts_.empty()) { - DCHECK(!font_description_string_.empty()); - - std::vector<std::string> font_names; - ParseFontDescriptionString(font_description_string_, &font_names, - &font_style_, &font_size_); - for (size_t i = 0; i < font_names.size(); ++i) { - DCHECK(!font_names[i].empty()); - - Font font(font_names[i], font_size_); - if (font_style_ == Font::NORMAL) - fonts_.push_back(font); - else - fonts_.push_back(font.DeriveFont(0, font_style_)); - } - } - return fonts_; + return impl_->GetFonts(); } const Font& FontList::GetPrimaryFont() const { - return GetFonts()[0]; + return impl_->GetPrimaryFont(); } -void FontList::CacheCommonFontHeightAndBaseline() const { - int ascent = 0; - int descent = 0; - const std::vector<Font>& fonts = GetFonts(); - for (std::vector<Font>::const_iterator i = fonts.begin(); - i != fonts.end(); ++i) { - ascent = std::max(ascent, i->GetBaseline()); - descent = std::max(descent, i->GetHeight() - i->GetBaseline()); - } - common_height_ = ascent + descent; - common_baseline_ = ascent; -} +FontList::FontList(FontListImpl* impl) : impl_(impl) {} + +// static +const scoped_refptr<FontListImpl>& FontList::GetDefaultImpl() { + // SetDefaultFontDescription() must be called and the default font description + // must be set earlier than any call of this function. + DCHECK(!(g_default_font_description == NULL)) // != is not overloaded. + << "SetDefaultFontDescription has not been called."; -void FontList::CacheFontStyleAndSize() const { - if (!fonts_.empty()) { - font_style_ = fonts_[0].GetStyle(); - font_size_ = fonts_[0].GetFontSize(); - } else { - std::vector<std::string> font_names; - ParseFontDescriptionString(font_description_string_, &font_names, - &font_style_, &font_size_); + if (!g_default_impl_initialized) { + g_default_impl.Get() = + g_default_font_description.Get().empty() ? + new FontListImpl(Font()) : + new FontListImpl(g_default_font_description.Get()); + g_default_impl_initialized = true; } + + return g_default_impl.Get(); } } // namespace gfx diff --git a/chromium/ui/gfx/font_list.h b/chromium/ui/gfx/font_list.h index e37fd625a23..1ba8ad1aff9 100644 --- a/chromium/ui/gfx/font_list.h +++ b/chromium/ui/gfx/font_list.h @@ -8,35 +8,35 @@ #include <string> #include <vector> +#include "base/memory/ref_counted.h" #include "ui/gfx/font.h" #include "ui/gfx/gfx_export.h" namespace gfx { -// FontList represents a list of fonts either in the form of Font vector or in -// the form of a string representing font names, styles, and size. -// -// The string representation is in the form "FAMILY_LIST [STYLE_OPTIONS] SIZE", -// where FAMILY_LIST is a comma separated list of families terminated by a -// comma, STYLE_OPTIONS is a whitespace separated list of words where each word -// describes one of style, variant, weight, stretch, or gravity, and SIZE is -// a decimal number followed by "px" for absolute size. STYLE_OPTIONS may be -// absent. +class FontListImpl; + +// FontList represents a list of fonts and provides metrics which are common +// in the fonts. FontList is copyable and it's quite cheap to copy. // -// The string format complies with that of Pango detailed at +// The format of font description string complies with that of Pango detailed at // http://developer.gnome.org/pango/stable/pango-Fonts.html#pango-font-description-from-string -// -// FontList could be initialized either way without conversion to the other -// form. The conversion to the other form is done only when asked to get the -// other form. -// -// FontList allows operator= since FontList is a data member type in RenderText, -// and operator= is used in RenderText::SetFontList(). +// The format is "<FONT_FAMILY_LIST>,[STYLES] <SIZE>" where +// FONT_FAMILY_LIST is a comma-separated list of font family names, +// STYLES is a space-separated list of style names ("Bold" and "Italic"), +// SIZE is a font size in pixel with the suffix "px". +// Here are examples of font description string: +// "Arial, Helvetica, Bold Italic 14px" +// "Arial, 14px" class GFX_EXPORT FontList { public: - // Creates a font list with a Font with default name and style. + // Creates a font list with default font names, size and style, which are + // specified by SetDefaultFontDescription(). FontList(); + // Creates a font list that is a clone of another font list. + FontList(const FontList& other); + // Creates a font list from a string representing font names, styles, and // size. explicit FontList(const std::string& font_description_string); @@ -55,6 +55,9 @@ class GFX_EXPORT FontList { ~FontList(); + // Copies the given font list into this object. + FontList& operator=(const FontList& other); + // Sets the description string for default FontList construction. If it's // empty, FontList will initialize using the default Font constructor. // @@ -65,23 +68,20 @@ class GFX_EXPORT FontList { // is changed. static void SetDefaultFontDescription(const std::string& font_description); - // Returns a new FontList with the given |font_style| flags. - FontList DeriveFontList(int font_style) const; - - // Returns a new FontList with the same font names and style but with the - // given font |size| in pixels. - FontList DeriveFontListWithSize(int size) const; - - // Returns a new FontList with the same font names and style but resized. - // |size_delta| is the size in pixels to add to the current font size. - FontList DeriveFontListWithSizeDelta(int size_delta) const; - // Returns a new FontList with the same font names but resized and the given // style. |size_delta| is the size in pixels to add to the current font size. // |font_style| specifies the new style, which is a bitmask of the values: // Font::BOLD, Font::ITALIC and Font::UNDERLINE. - FontList DeriveFontListWithSizeDeltaAndStyle(int size_delta, - int font_style) const; + FontList Derive(int size_delta, int font_style) const; + + // Returns a new FontList with the same font names and style but resized. + // |size_delta| is the size in pixels to add to the current font size. + FontList DeriveWithSizeDelta(int size_delta) const; + + // Returns a new FontList with the same font names and size but the given + // style. |font_style| specifies the new style, which is a bitmask of the + // values: Font::BOLD, Font::ITALIC and Font::UNDERLINE. + FontList DeriveWithStyle(int font_style) const; // Returns the height of this font list, which is max(ascent) + max(descent) // for all the fonts in the font list. @@ -95,9 +95,6 @@ class GFX_EXPORT FontList { // Currently returns the cap height of the primary font. int GetCapHeight() const; - // Returns the number of horizontal pixels needed to display |text|. - int GetStringWidth(const base::string16& text) const; - // Returns the expected number of horizontal pixels needed to display the // specified length of characters. Call GetStringWidth() to retrieve the // actual number. @@ -121,33 +118,11 @@ class GFX_EXPORT FontList { const Font& GetPrimaryFont() const; private: - // Extracts common font height and baseline into |common_height_| and - // |common_baseline_|. - void CacheCommonFontHeightAndBaseline() const; - - // Extracts font style and size into |font_style_| and |font_size_|. - void CacheFontStyleAndSize() const; - - // A vector of Font. If FontList is constructed with font description string, - // |fonts_| is not initialized during construction. Instead, it is computed - // lazily when user asked to get the font vector. - mutable std::vector<Font> fonts_; - - // A string representing font names, styles, and sizes. - // Please refer to the comments before class declaration for details on string - // format. - // If FontList is constructed with a vector of font, - // |font_description_string_| is not initialized during construction. Instead, - // it is computed lazily when user asked to get the font description string. - mutable std::string font_description_string_; - - // The cached common height and baseline of the fonts in the font list. - mutable int common_height_; - mutable int common_baseline_; - - // Cached font style and size. - mutable int font_style_; - mutable int font_size_; + explicit FontList(FontListImpl* impl); + + static const scoped_refptr<FontListImpl>& GetDefaultImpl(); + + scoped_refptr<FontListImpl> impl_; }; } // namespace gfx diff --git a/chromium/ui/gfx/font_list_impl.cc b/chromium/ui/gfx/font_list_impl.cc new file mode 100644 index 00000000000..8c00fcc8278 --- /dev/null +++ b/chromium/ui/gfx/font_list_impl.cc @@ -0,0 +1,248 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/gfx/font_list_impl.h" + +#include <algorithm> + +#include "base/logging.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_split.h" +#include "base/strings/string_util.h" +#include "ui/gfx/font.h" + +namespace { + +// Parses font description into |font_names|, |font_style| and |font_size|. +void ParseFontDescriptionString(const std::string& font_description_string, + std::vector<std::string>* font_names, + int* font_style, + int* font_size) { + base::SplitString(font_description_string, ',', font_names); + DCHECK_GT(font_names->size(), 1U); + + // The last item is [STYLE_OPTIONS] SIZE. + std::vector<std::string> styles_size; + base::SplitString(font_names->back(), ' ', &styles_size); + DCHECK(!styles_size.empty()); + base::StringToInt(styles_size.back(), font_size); + DCHECK_GT(*font_size, 0); + font_names->pop_back(); + + // Font supports BOLD and ITALIC; underline is supported via RenderText. + *font_style = 0; + for (size_t i = 0; i < styles_size.size() - 1; ++i) { + // Styles are separated by white spaces. base::SplitString splits styles + // by space, and it inserts empty string for continuous spaces. + if (styles_size[i].empty()) + continue; + if (!styles_size[i].compare("Bold")) + *font_style |= gfx::Font::BOLD; + else if (!styles_size[i].compare("Italic")) + *font_style |= gfx::Font::ITALIC; + else + NOTREACHED(); + } +} + +// Returns the font style and size as a string. +std::string FontStyleAndSizeToString(int font_style, int font_size) { + std::string result; + if (font_style & gfx::Font::BOLD) + result += "Bold "; + if (font_style & gfx::Font::ITALIC) + result += "Italic "; + result += base::IntToString(font_size); + result += "px"; + return result; +} + +// Returns font description from |font_names|, |font_style|, and |font_size|. +std::string BuildFontDescription(const std::vector<std::string>& font_names, + int font_style, + int font_size) { + std::string description = JoinString(font_names, ','); + description += "," + FontStyleAndSizeToString(font_style, font_size); + return description; +} + +} // namespace + +namespace gfx { + +FontListImpl::FontListImpl(const std::string& font_description_string) + : font_description_string_(font_description_string), + common_height_(-1), + common_baseline_(-1), + font_style_(-1), + font_size_(-1) { + DCHECK(!font_description_string.empty()); + // DCHECK description string ends with "px" for size in pixel. + DCHECK(EndsWith(font_description_string, "px", true)); +} + +FontListImpl::FontListImpl(const std::vector<std::string>& font_names, + int font_style, + int font_size) + : font_description_string_(BuildFontDescription(font_names, font_style, + font_size)), + common_height_(-1), + common_baseline_(-1), + font_style_(font_style), + font_size_(font_size) { + DCHECK(!font_names.empty()); + DCHECK(!font_names[0].empty()); +} + +FontListImpl::FontListImpl(const std::vector<Font>& fonts) + : fonts_(fonts), + common_height_(-1), + common_baseline_(-1), + font_style_(-1), + font_size_(-1) { + DCHECK(!fonts.empty()); + font_style_ = fonts[0].GetStyle(); + font_size_ = fonts[0].GetFontSize(); +#if DCHECK_IS_ON + for (size_t i = 1; i < fonts.size(); ++i) { + DCHECK_EQ(fonts[i].GetStyle(), font_style_); + DCHECK_EQ(fonts[i].GetFontSize(), font_size_); + } +#endif +} + +FontListImpl::FontListImpl(const Font& font) + : common_height_(-1), + common_baseline_(-1), + font_style_(-1), + font_size_(-1) { + fonts_.push_back(font); +} + +FontListImpl* FontListImpl::Derive(int size_delta, int font_style) const { + // If there is a font vector, derive from that. + if (!fonts_.empty()) { + std::vector<Font> fonts = fonts_; + for (size_t i = 0; i < fonts.size(); ++i) + fonts[i] = fonts[i].Derive(size_delta, font_style); + return new FontListImpl(fonts); + } + + // Otherwise, parse the font description string to derive from it. + std::vector<std::string> font_names; + int old_size; + int old_style; + ParseFontDescriptionString(font_description_string_, &font_names, + &old_style, &old_size); + const int size = std::max(1, old_size + size_delta); + return new FontListImpl(font_names, font_style, size); +} + +int FontListImpl::GetHeight() const { + if (common_height_ == -1) + CacheCommonFontHeightAndBaseline(); + return common_height_; +} + +int FontListImpl::GetBaseline() const { + if (common_baseline_ == -1) + CacheCommonFontHeightAndBaseline(); + return common_baseline_; +} + +int FontListImpl::GetCapHeight() const { + // Assume the primary font is used to render Latin characters. + return GetPrimaryFont().GetCapHeight(); +} + +int FontListImpl::GetExpectedTextWidth(int length) const { + // Rely on the primary font metrics for the time being. + return GetPrimaryFont().GetExpectedTextWidth(length); +} + +int FontListImpl::GetFontStyle() const { + if (font_style_ == -1) + CacheFontStyleAndSize(); + return font_style_; +} + +const std::string& FontListImpl::GetFontDescriptionString() const { + if (font_description_string_.empty()) { + DCHECK(!fonts_.empty()); + for (size_t i = 0; i < fonts_.size(); ++i) { + std::string name = fonts_[i].GetFontName(); + font_description_string_ += name; + font_description_string_ += ','; + } + // All fonts have the same style and size. + font_description_string_ += + FontStyleAndSizeToString(fonts_[0].GetStyle(), fonts_[0].GetFontSize()); + } + return font_description_string_; +} + +int FontListImpl::GetFontSize() const { + if (font_size_ == -1) + CacheFontStyleAndSize(); + return font_size_; +} + +const std::vector<Font>& FontListImpl::GetFonts() const { + if (fonts_.empty()) { + DCHECK(!font_description_string_.empty()); + + std::vector<std::string> font_names; + // It's possible that gfx::Font::UNDERLINE is specified and it's already + // stored in |font_style_| but |font_description_string_| doesn't have the + // underline info. So we should respect |font_style_| as long as it's + // valid. + int style = 0; + ParseFontDescriptionString(font_description_string_, &font_names, + &style, &font_size_); + if (font_style_ == -1) + font_style_ = style; + for (size_t i = 0; i < font_names.size(); ++i) { + DCHECK(!font_names[i].empty()); + + Font font(font_names[i], font_size_); + if (font_style_ == Font::NORMAL) + fonts_.push_back(font); + else + fonts_.push_back(font.Derive(0, font_style_)); + } + } + return fonts_; +} + +const Font& FontListImpl::GetPrimaryFont() const { + return GetFonts()[0]; +} + +FontListImpl::~FontListImpl() {} + +void FontListImpl::CacheCommonFontHeightAndBaseline() const { + int ascent = 0; + int descent = 0; + const std::vector<Font>& fonts = GetFonts(); + for (std::vector<Font>::const_iterator i = fonts.begin(); + i != fonts.end(); ++i) { + ascent = std::max(ascent, i->GetBaseline()); + descent = std::max(descent, i->GetHeight() - i->GetBaseline()); + } + common_height_ = ascent + descent; + common_baseline_ = ascent; +} + +void FontListImpl::CacheFontStyleAndSize() const { + if (!fonts_.empty()) { + font_style_ = fonts_[0].GetStyle(); + font_size_ = fonts_[0].GetFontSize(); + } else { + std::vector<std::string> font_names; + ParseFontDescriptionString(font_description_string_, &font_names, + &font_style_, &font_size_); + } +} + +} // namespace gfx diff --git a/chromium/ui/gfx/font_list_impl.h b/chromium/ui/gfx/font_list_impl.h new file mode 100644 index 00000000000..94bb33036ab --- /dev/null +++ b/chromium/ui/gfx/font_list_impl.h @@ -0,0 +1,123 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_GFX_FONT_LIST_IMPL_H_ +#define UI_GFX_FONT_LIST_IMPL_H_ + +#include <string> +#include <vector> + +#include "base/memory/ref_counted.h" + +namespace gfx { + +class Font; + +// FontListImpl is designed to provide the implementation of FontList and +// intended to be used only from FontList. You must not use this class +// directly. +// +// FontListImpl represents a list of fonts either in the form of Font vector or +// in the form of a string representing font names, styles, and size. +// +// FontListImpl could be initialized either way without conversion to the other +// form. The conversion to the other form is done only when asked to get the +// other form. +// +// For the format of font description string, see font_list.h for details. +class FontListImpl : public base::RefCounted<FontListImpl> { + public: + // Creates a font list from a string representing font names, styles, and + // size. + explicit FontListImpl(const std::string& font_description_string); + + // Creates a font list from font names, styles and size. + FontListImpl(const std::vector<std::string>& font_names, + int font_style, + int font_size); + + // Creates a font list from a Font vector. + // All fonts in this vector should have the same style and size. + explicit FontListImpl(const std::vector<Font>& fonts); + + // Creates a font list from a Font. + explicit FontListImpl(const Font& font); + + // Returns a new FontListImpl with the same font names but resized and the + // given style. |size_delta| is the size in pixels to add to the current font + // size. |font_style| specifies the new style, which is a bitmask of the + // values: Font::BOLD, Font::ITALIC and Font::UNDERLINE. + FontListImpl* Derive(int size_delta, int font_style) const; + + // Returns the height of this font list, which is max(ascent) + max(descent) + // for all the fonts in the font list. + int GetHeight() const; + + // Returns the baseline of this font list, which is max(baseline) for all the + // fonts in the font list. + int GetBaseline() const; + + // Returns the cap height of this font list. + // Currently returns the cap height of the primary font. + int GetCapHeight() const; + + // Returns the expected number of horizontal pixels needed to display the + // specified length of characters. Call GetStringWidth() to retrieve the + // actual number. + int GetExpectedTextWidth(int length) const; + + // Returns the |gfx::Font::FontStyle| style flags for this font list. + int GetFontStyle() const; + + // Returns a string representing font names, styles, and size. If the + // FontListImpl is initialized by a vector of Font, use the first font's style + // and size for the description. + const std::string& GetFontDescriptionString() const; + + // Returns the font size in pixels. + int GetFontSize() const; + + // Returns the Font vector. + const std::vector<Font>& GetFonts() const; + + // Returns the first font in the list. + const Font& GetPrimaryFont() const; + + private: + friend class base::RefCounted<FontListImpl>; + + ~FontListImpl(); + + // Extracts common font height and baseline into |common_height_| and + // |common_baseline_|. + void CacheCommonFontHeightAndBaseline() const; + + // Extracts font style and size into |font_style_| and |font_size_|. + void CacheFontStyleAndSize() const; + + // A vector of Font. If FontListImpl is constructed with font description + // string, |fonts_| is not initialized during construction. Instead, it is + // computed lazily when user asked to get the font vector. + mutable std::vector<Font> fonts_; + + // A string representing font names, styles, and sizes. + // Please refer to the comments before class declaration for details on string + // format. + // If FontListImpl is constructed with a vector of font, + // |font_description_string_| is not initialized during construction. Instead, + // it is computed lazily when user asked to get the font description string. + mutable std::string font_description_string_; + + // The cached common height and baseline of the fonts in the font list. + mutable int common_height_; + mutable int common_baseline_; + + // Cached font style and size. + mutable int font_style_; + mutable int font_size_; +}; + +} // namespace gfx + +#endif // UI_GFX_FONT_LIST_IMPL_H_ diff --git a/chromium/ui/gfx/font_list_unittest.cc b/chromium/ui/gfx/font_list_unittest.cc index 6cbb41fbecd..b96de46318b 100644 --- a/chromium/ui/gfx/font_list_unittest.cc +++ b/chromium/ui/gfx/font_list_unittest.cc @@ -24,6 +24,8 @@ std::string FontToString(const gfx::Font& font) { font_string += "|bold"; if (style & gfx::Font::ITALIC) font_string += "|italic"; + if (style & gfx::Font::UNDERLINE) + font_string += "|underline"; return font_string; } @@ -43,9 +45,10 @@ TEST(FontListTest, FontDescString_FromFontNamesStyleAndSize) { std::vector<std::string> font_names; font_names.push_back("Arial"); font_names.push_back("Droid Sans serif"); - int font_style = Font::BOLD | Font::ITALIC; + int font_style = Font::BOLD | Font::ITALIC | Font::UNDERLINE; int font_size = 11; FontList font_list = FontList(font_names, font_style, font_size); + // "Underline" doesn't appear in the font description string. EXPECT_EQ("Arial,Droid Sans serif,Bold Italic 11px", font_list.GetFontDescriptionString()); } @@ -60,11 +63,15 @@ TEST(FontListTest, FontDescString_FromFont) { TEST(FontListTest, FontDescString_FromFontWithNonNormalStyle) { // Test init from Font with non-normal style. Font font("Arial", 8); - FontList font_list = FontList(font.DeriveFont(2, Font::BOLD)); + FontList font_list = FontList(font.Derive(2, Font::BOLD)); EXPECT_EQ("Arial,Bold 10px", font_list.GetFontDescriptionString()); - font_list = FontList(font.DeriveFont(-2, Font::ITALIC)); + font_list = FontList(font.Derive(-2, Font::ITALIC)); EXPECT_EQ("Arial,Italic 6px", font_list.GetFontDescriptionString()); + + // "Underline" doesn't appear in the font description string. + font_list = FontList(font.Derive(-4, Font::UNDERLINE)); + EXPECT_EQ("Arial,4px", font_list.GetFontDescriptionString()); } TEST(FontListTest, FontDescString_FromFontVector) { @@ -72,8 +79,8 @@ TEST(FontListTest, FontDescString_FromFontVector) { Font font("Arial", 8); Font font_1("Sans serif", 10); std::vector<Font> fonts; - fonts.push_back(font.DeriveFont(0, Font::BOLD)); - fonts.push_back(font_1.DeriveFont(-2, Font::BOLD)); + fonts.push_back(font.Derive(0, Font::BOLD)); + fonts.push_back(font_1.Derive(-2, Font::BOLD)); FontList font_list = FontList(fonts); EXPECT_EQ("Arial,Sans serif,Bold 8px", font_list.GetFontDescriptionString()); } @@ -118,12 +125,12 @@ TEST(FontListTest, Fonts_FromFont) { TEST(FontListTest, Fonts_FromFontWithNonNormalStyle) { // Test init from Font with non-normal style. Font font("Arial", 8); - FontList font_list = FontList(font.DeriveFont(2, Font::BOLD)); + FontList font_list = FontList(font.Derive(2, Font::BOLD)); std::vector<Font> fonts = font_list.GetFonts(); EXPECT_EQ(1U, fonts.size()); EXPECT_EQ("Arial|10|bold", FontToString(fonts[0])); - font_list = FontList(font.DeriveFont(-2, Font::ITALIC)); + font_list = FontList(font.Derive(-2, Font::ITALIC)); fonts = font_list.GetFonts(); EXPECT_EQ(1U, fonts.size()); EXPECT_EQ("Arial|6|italic", FontToString(fonts[0])); @@ -134,8 +141,8 @@ TEST(FontListTest, Fonts_FromFontVector) { Font font("Arial", 8); Font font_1("Sans serif", 10); std::vector<Font> input_fonts; - input_fonts.push_back(font.DeriveFont(0, Font::BOLD)); - input_fonts.push_back(font_1.DeriveFont(-2, Font::BOLD)); + input_fonts.push_back(font.Derive(0, Font::BOLD)); + input_fonts.push_back(font_1.Derive(-2, Font::BOLD)); FontList font_list = FontList(input_fonts); const std::vector<Font>& fonts = font_list.GetFonts(); EXPECT_EQ(2U, fonts.size()); @@ -161,8 +168,8 @@ TEST(FontListTest, Fonts_FontVector_RoundTrip) { Font font("Arial", 8); Font font_1("Sans serif", 10); std::vector<Font> input_fonts; - input_fonts.push_back(font.DeriveFont(0, Font::BOLD)); - input_fonts.push_back(font_1.DeriveFont(-2, Font::BOLD)); + input_fonts.push_back(font.Derive(0, Font::BOLD)); + input_fonts.push_back(font_1.Derive(-2, Font::BOLD)); FontList font_list = FontList(input_fonts); const std::string& desc_string = font_list.GetFontDescriptionString(); @@ -194,71 +201,54 @@ TEST(FontListTest, Fonts_GetStyle) { fonts.push_back(gfx::Font("Sans serif", 8)); FontList font_list = FontList(fonts); EXPECT_EQ(Font::NORMAL, font_list.GetFontStyle()); - fonts[0] = fonts[0].DeriveFont(0, Font::ITALIC | Font::BOLD); - fonts[1] = fonts[1].DeriveFont(0, Font::ITALIC | Font::BOLD); + fonts[0] = fonts[0].Derive(0, Font::ITALIC | Font::BOLD); + fonts[1] = fonts[1].Derive(0, Font::ITALIC | Font::BOLD); font_list = FontList(fonts); EXPECT_EQ(Font::ITALIC | Font::BOLD, font_list.GetFontStyle()); } -TEST(FontListTest, FontDescString_DeriveFontList) { - FontList font_list = FontList("Arial,Sans serif, 8px"); - - FontList derived = font_list.DeriveFontList(Font::BOLD | Font::ITALIC); - EXPECT_EQ("Arial,Sans serif,Bold Italic 8px", - derived.GetFontDescriptionString()); -} - -TEST(FontListTest, Fonts_DeriveFontList) { - std::vector<Font> fonts; - fonts.push_back(gfx::Font("Arial", 8)); - fonts.push_back(gfx::Font("Sans serif", 8)); - FontList font_list = FontList(fonts); - - FontList derived = font_list.DeriveFontList(Font::BOLD | Font::ITALIC); - const std::vector<Font>& derived_fonts = derived.GetFonts(); - - EXPECT_EQ(2U, derived_fonts.size()); - EXPECT_EQ("Arial|8|bold|italic", FontToString(derived_fonts[0])); - EXPECT_EQ("Sans serif|8|bold|italic", FontToString(derived_fonts[1])); -} +TEST(FontListTest, FontDescString_Derive) { + FontList font_list = FontList("Arial,Sans serif,Bold Italic 8px"); -TEST(FontListTest, FontDescString_DeriveFontListWithSize) { - FontList font_list = FontList("Arial,Sans serif,Bold Italic 8px"); + FontList derived = font_list.Derive(10, Font::ITALIC | Font::UNDERLINE); + EXPECT_EQ("Arial,Sans serif,Italic 18px", derived.GetFontDescriptionString()); + EXPECT_EQ(Font::ITALIC | Font::UNDERLINE, derived.GetFontStyle()); - FontList derived = font_list.DeriveFontListWithSize(10); - EXPECT_EQ("Arial,Sans serif,Bold Italic 10px", - derived.GetFontDescriptionString()); + // FontList has a special case for Font::UNDERLINE. See if the handling of + // Font::UNDERLINE in GetFonts() is okay or not. + derived.GetFonts(); + EXPECT_EQ(Font::ITALIC | Font::UNDERLINE, derived.GetFontStyle()); } -TEST(FontListTest, Fonts_DeriveFontListWithSize) { +TEST(FontListTest, Fonts_Derive) { std::vector<Font> fonts; fonts.push_back(gfx::Font("Arial", 8)); fonts.push_back(gfx::Font("Sans serif", 8)); FontList font_list = FontList(fonts); - FontList derived = font_list.DeriveFontListWithSize(5); + FontList derived = font_list.Derive(5, Font::BOLD | Font::UNDERLINE); const std::vector<Font>& derived_fonts = derived.GetFonts(); EXPECT_EQ(2U, derived_fonts.size()); - EXPECT_EQ("Arial|5", FontToString(derived_fonts[0])); - EXPECT_EQ("Sans serif|5", FontToString(derived_fonts[1])); + EXPECT_EQ("Arial|13|bold|underline", FontToString(derived_fonts[0])); + EXPECT_EQ("Sans serif|13|bold|underline", FontToString(derived_fonts[1])); } -TEST(FontListTest, FontDescString_DeriveFontListWithSizeDelta) { +TEST(FontListTest, FontDescString_DeriveWithSizeDelta) { FontList font_list = FontList("Arial,Sans serif,Bold 18px"); - FontList derived = font_list.DeriveFontListWithSizeDelta(-8); + FontList derived = font_list.DeriveWithSizeDelta(-8); EXPECT_EQ("Arial,Sans serif,Bold 10px", derived.GetFontDescriptionString()); } -TEST(FontListTest, Fonts_DeriveFontListWithSizeDelta) { +TEST(FontListTest, Fonts_DeriveWithSizeDelta) { std::vector<Font> fonts; - fonts.push_back(gfx::Font("Arial", 18).DeriveFont(0, Font::ITALIC)); - fonts.push_back(gfx::Font("Sans serif", 18).DeriveFont(0, Font::ITALIC)); + fonts.push_back(gfx::Font("Arial", 18).Derive(0, Font::ITALIC)); + fonts.push_back(gfx::Font("Sans serif", 18).Derive(0, Font::ITALIC)); FontList font_list = FontList(fonts); - FontList derived = font_list.DeriveFontListWithSizeDelta(-5); + FontList derived = font_list.DeriveWithSizeDelta(-5); const std::vector<Font>& derived_fonts = derived.GetFonts(); EXPECT_EQ(2U, derived_fonts.size()); @@ -266,30 +256,6 @@ TEST(FontListTest, Fonts_DeriveFontListWithSizeDelta) { EXPECT_EQ("Sans serif|13|italic", FontToString(derived_fonts[1])); } -TEST(FontListTest, FontDescString_DeriveFontListWithSizeDeltaAndStyle) { - FontList font_list = FontList("Arial,Sans serif,Bold Italic 8px"); - - FontList derived = - font_list.DeriveFontListWithSizeDeltaAndStyle(10, Font::ITALIC); - EXPECT_EQ("Arial,Sans serif,Italic 18px", - derived.GetFontDescriptionString()); -} - -TEST(FontListTest, Fonts_DeriveFontListWithSizeDeltaAndStyle) { - std::vector<Font> fonts; - fonts.push_back(gfx::Font("Arial", 8)); - fonts.push_back(gfx::Font("Sans serif", 8)); - FontList font_list = FontList(fonts); - - FontList derived = - font_list.DeriveFontListWithSizeDeltaAndStyle(5, Font::BOLD); - const std::vector<Font>& derived_fonts = derived.GetFonts(); - - EXPECT_EQ(2U, derived_fonts.size()); - EXPECT_EQ("Arial|13|bold", FontToString(derived_fonts[0])); - EXPECT_EQ("Sans serif|13|bold", FontToString(derived_fonts[1])); -} - TEST(FontListTest, Fonts_GetHeight_GetBaseline) { // If a font list has only one font, the height and baseline must be the same. Font font1("Arial", 16); diff --git a/chromium/ui/gfx/font_render_params_linux.cc b/chromium/ui/gfx/font_render_params_linux.cc index 2fb369a5d4c..fdef6a9926f 100644 --- a/chromium/ui/gfx/font_render_params_linux.cc +++ b/chromium/ui/gfx/font_render_params_linux.cc @@ -6,12 +6,13 @@ #include "base/command_line.h" #include "base/logging.h" +#include "ui/gfx/display.h" #include "ui/gfx/switches.h" -#if defined(TOOLKIT_GTK) -#include <gtk/gtk.h> -#else #include <fontconfig/fontconfig.h> + +#if defined(OS_LINUX) && defined(USE_AURA) && !defined(OS_CHROMEOS) +#include "ui/gfx/linux_font_delegate.h" #endif namespace gfx { @@ -19,67 +20,21 @@ namespace gfx { namespace { bool SubpixelPositioningRequested(bool renderer) { - return CommandLine::ForCurrentProcess()->HasSwitch( - renderer ? - switches::kEnableWebkitTextSubpixelPositioning : - switches::kEnableBrowserTextSubpixelPositioning); + const CommandLine* cl = CommandLine::ForCurrentProcess(); + if (renderer) { + // Text rendered by Blink in high-DPI mode is poorly-hinted unless subpixel + // positioning is used (as opposed to each glyph being individually snapped + // to the pixel grid). + return cl->HasSwitch(switches::kEnableWebkitTextSubpixelPositioning) || + (Display::HasForceDeviceScaleFactor() && + Display::GetForcedDeviceScaleFactor() != 1.0); + } + return cl->HasSwitch(switches::kEnableBrowserTextSubpixelPositioning); } // Initializes |params| with the system's default settings. |renderer| is true // when setting WebKit renderer defaults. void LoadDefaults(FontRenderParams* params, bool renderer) { -#if defined(TOOLKIT_GTK) - params->antialiasing = true; - // TODO(wangxianzhu): autohinter is now true to keep original behavior - // of WebKit, but it might not be the best value. - params->autohinter = true; - params->use_bitmaps = true; - params->hinting = FontRenderParams::HINTING_SLIGHT; - params->subpixel_rendering = FontRenderParams::SUBPIXEL_RENDERING_NONE; - - GtkSettings* gtk_settings = gtk_settings_get_default(); - CHECK(gtk_settings); - gint gtk_antialias = 0; - gint gtk_hinting = 0; - gchar* gtk_hint_style = NULL; - gchar* gtk_rgba = NULL; - g_object_get(gtk_settings, - "gtk-xft-antialias", >k_antialias, - "gtk-xft-hinting", >k_hinting, - "gtk-xft-hintstyle", >k_hint_style, - "gtk-xft-rgba", >k_rgba, - NULL); - - // g_object_get() doesn't tell us whether the properties were present or not, - // but if they aren't (because gnome-settings-daemon isn't running), we'll get - // NULL values for the strings. - if (gtk_hint_style && gtk_rgba) { - params->antialiasing = gtk_antialias; - - if (gtk_hinting == 0 || strcmp(gtk_hint_style, "hintnone") == 0) - params->hinting = FontRenderParams::HINTING_NONE; - else if (strcmp(gtk_hint_style, "hintslight") == 0) - params->hinting = FontRenderParams::HINTING_SLIGHT; - else if (strcmp(gtk_hint_style, "hintmedium") == 0) - params->hinting = FontRenderParams::HINTING_MEDIUM; - else if (strcmp(gtk_hint_style, "hintfull") == 0) - params->hinting = FontRenderParams::HINTING_FULL; - - if (strcmp(gtk_rgba, "none") == 0) - params->subpixel_rendering = FontRenderParams::SUBPIXEL_RENDERING_NONE; - else if (strcmp(gtk_rgba, "rgb") == 0) - params->subpixel_rendering = FontRenderParams::SUBPIXEL_RENDERING_RGB; - else if (strcmp(gtk_rgba, "bgr") == 0) - params->subpixel_rendering = FontRenderParams::SUBPIXEL_RENDERING_BGR; - else if (strcmp(gtk_rgba, "vrgb") == 0) - params->subpixel_rendering = FontRenderParams::SUBPIXEL_RENDERING_VRGB; - else if (strcmp(gtk_rgba, "vbgr") == 0) - params->subpixel_rendering = FontRenderParams::SUBPIXEL_RENDERING_VBGR; - } - - g_free(gtk_hint_style); - g_free(gtk_rgba); -#else // For non-GTK builds (read: Aura), just use reasonable hardcoded values. params->antialiasing = true; params->autohinter = true; @@ -114,6 +69,14 @@ void LoadDefaults(FontRenderParams* params, bool renderer) { default: params->subpixel_rendering = FontRenderParams::SUBPIXEL_RENDERING_NONE; } + +#if defined(OS_LINUX) && defined(USE_AURA) && !defined(OS_CHROMEOS) + const LinuxFontDelegate* delegate = LinuxFontDelegate::instance(); + if (delegate) { + params->antialiasing = delegate->UseAntialiasing(); + params->hinting = delegate->GetHintingStyle(); + params->subpixel_rendering = delegate->GetSubpixelRenderingStyle(); + } #endif params->subpixel_positioning = SubpixelPositioningRequested(renderer); diff --git a/chromium/ui/gfx/font_smoothing_win.cc b/chromium/ui/gfx/font_smoothing_win.cc index b181fb564e8..78309ff3d96 100644 --- a/chromium/ui/gfx/font_smoothing_win.cc +++ b/chromium/ui/gfx/font_smoothing_win.cc @@ -36,7 +36,7 @@ class CachedFontSmoothingSettings : public gfx::SingletonHwnd::Observer { // Queries the font settings from the system. void QueryFontSettings(); - // Indicates whether the MessagePumpObserver has been registered. + // Indicates whether the SingletonHwnd::Observer has been registered. bool observer_added_; // Indicates whether |smoothing_enabled_| and |cleartype_enabled_| are valid diff --git a/chromium/ui/gfx/font_unittest.cc b/chromium/ui/gfx/font_unittest.cc index 2fdaab872f4..da4bdfcc4bd 100644 --- a/chromium/ui/gfx/font_unittest.cc +++ b/chromium/ui/gfx/font_unittest.cc @@ -70,7 +70,7 @@ TEST_F(FontTest, LoadArial) { TEST_F(FontTest, LoadArialBold) { Font cf("Arial", 16); - Font bold(cf.DeriveFont(0, Font::BOLD)); + Font bold(cf.Derive(0, Font::BOLD)); NativeFont native = bold.GetNativeFont(); EXPECT_TRUE(native); EXPECT_EQ(bold.GetStyle(), Font::BOLD); @@ -95,11 +95,7 @@ TEST_F(FontTest, CapHeight) { Font cf("Arial", 16); EXPECT_GT(cf.GetCapHeight(), 0); EXPECT_GT(cf.GetCapHeight(), cf.GetHeight() / 2); -#if defined(OS_CHROMEOS) || defined(OS_LINUX) - EXPECT_EQ(cf.GetCapHeight(), cf.GetBaseline()); -#else EXPECT_LT(cf.GetCapHeight(), cf.GetBaseline()); -#endif } TEST_F(FontTest, AvgWidths) { @@ -110,30 +106,26 @@ TEST_F(FontTest, AvgWidths) { EXPECT_GT(cf.GetExpectedTextWidth(3), cf.GetExpectedTextWidth(2)); } -TEST_F(FontTest, AvgCharWidth) { - Font cf("Arial", 16); - EXPECT_GT(cf.GetAverageCharacterWidth(), 0); -} - -TEST_F(FontTest, Widths) { - Font cf("Arial", 16); - EXPECT_EQ(cf.GetStringWidth(base::string16()), 0); - EXPECT_GT(cf.GetStringWidth(ASCIIToUTF16("a")), - cf.GetStringWidth(base::string16())); - EXPECT_GT(cf.GetStringWidth(ASCIIToUTF16("ab")), - cf.GetStringWidth(ASCIIToUTF16("a"))); - EXPECT_GT(cf.GetStringWidth(ASCIIToUTF16("abc")), - cf.GetStringWidth(ASCIIToUTF16("ab"))); -} - #if !defined(OS_WIN) // On Windows, Font::GetActualFontNameForTesting() doesn't work well for now. // http://crbug.com/327287 +// +// Check that fonts used for testing are installed and enabled. On Mac +// fonts may be installed but still need enabling in Font Book.app. +// http://crbug.com/347429 TEST_F(FontTest, GetActualFontNameForTesting) { Font arial("Arial", 16); - EXPECT_EQ("arial", StringToLowerASCII(arial.GetActualFontNameForTesting())); + EXPECT_EQ("arial", StringToLowerASCII(arial.GetActualFontNameForTesting())) + << "********\n" + << "Your test environment seems to be missing Arial font, which is " + << "needed for unittests. Check if Arial font is installed.\n" + << "********"; Font symbol("Symbol", 16); - EXPECT_EQ("symbol", StringToLowerASCII(symbol.GetActualFontNameForTesting())); + EXPECT_EQ("symbol", StringToLowerASCII(symbol.GetActualFontNameForTesting())) + << "********\n" + << "Your test environment seems to be missing Symbol font, which is " + << "needed for unittests. Check if Symbol font is installed.\n" + << "********"; const char* const invalid_font_name = "no_such_font_name"; Font fallback_font(invalid_font_name, 16); @@ -143,21 +135,21 @@ TEST_F(FontTest, GetActualFontNameForTesting) { #endif #if defined(OS_WIN) -TEST_F(FontTest, DeriveFontResizesIfSizeTooSmall) { +TEST_F(FontTest, DeriveResizesIfSizeTooSmall) { Font cf("Arial", 8); // The minimum font size is set to 5 in browser_main.cc. ScopedMinimumFontSizeCallback minimum_size(5); - Font derived_font = cf.DeriveFont(-4); + Font derived_font = cf.Derive(-4, cf.GetStyle()); EXPECT_EQ(5, derived_font.GetFontSize()); } -TEST_F(FontTest, DeriveFontKeepsOriginalSizeIfHeightOk) { +TEST_F(FontTest, DeriveKeepsOriginalSizeIfHeightOk) { Font cf("Arial", 8); // The minimum font size is set to 5 in browser_main.cc. ScopedMinimumFontSizeCallback minimum_size(5); - Font derived_font = cf.DeriveFont(-2); + Font derived_font = cf.Derive(-2, cf.GetStyle()); EXPECT_EQ(6, derived_font.GetFontSize()); } #endif // defined(OS_WIN) diff --git a/chromium/ui/gfx/gdi_util.cc b/chromium/ui/gfx/gdi_util.cc index 88c09355a28..dc7d83f37cf 100644 --- a/chromium/ui/gfx/gdi_util.cc +++ b/chromium/ui/gfx/gdi_util.cc @@ -85,8 +85,8 @@ HRGN ConvertPathToHRGN(const gfx::Path& path) { path.getPoints(points.get(), point_count); scoped_ptr<POINT[]> windows_points(new POINT[point_count]); for (int i = 0; i < point_count; ++i) { - windows_points[i].x = SkScalarRound(points[i].fX); - windows_points[i].y = SkScalarRound(points[i].fY); + windows_points[i].x = SkScalarRoundToInt(points[i].fX); + windows_points[i].y = SkScalarRoundToInt(points[i].fY); } return ::CreatePolygonRgn(windows_points.get(), point_count, ALTERNATE); diff --git a/chromium/ui/gfx/gdk_compat.h b/chromium/ui/gfx/gdk_compat.h deleted file mode 100644 index b11d7795831..00000000000 --- a/chromium/ui/gfx/gdk_compat.h +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_GFX_GDK_COMPAT_H_ -#define UI_GFX_GDK_COMPAT_H_ - -#include <gtk/gtk.h> -#include <gdk/gdkx.h> - -// Google Chrome must depend on GTK 2.18, at least until the next LTS drops -// (and we might have to extend which version of GTK we want to target due to -// RHEL). To make our porting job for GTK3 easier, we define all the methods -// that replace deprecated APIs in this file and then include it everywhere. -// -// This file is organized first by version, and then with each version, -// alphabetically by method. - -#if !GTK_CHECK_VERSION(2, 24, 0) -inline GdkWindow* gdk_x11_window_lookup_for_display(GdkDisplay* display, - Window window) { - return static_cast<GdkWindow*>(gdk_xid_table_lookup_for_display(display, - window)); -} -#endif - -#endif // UI_GFX_GDK_COMPAT_H_ diff --git a/chromium/ui/gfx/geometry/BUILD.gn b/chromium/ui/gfx/geometry/BUILD.gn new file mode 100644 index 00000000000..b7c877ac108 --- /dev/null +++ b/chromium/ui/gfx/geometry/BUILD.gn @@ -0,0 +1,62 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +component("geometry") { + sources = [ + "../gfx_export.h", + "box_f.cc", + "box_f.h", + "cubic_bezier.h", + "cubic_bezier.cc", + "insets.cc", + "insets.h", + "insets_base.h", + "insets_f.cc", + "insets_f.h", + "matrix3_f.cc", + "matrix3_f.h", + "point.cc", + "point.h", + "point3_f.cc", + "point3_f.h", + "point_base.h", + "point_conversions.cc", + "point_conversions.h", + "point_f.cc", + "point_f.h", + "quad_f.cc", + "quad_f.h", + "rect.cc", + "rect.h", + "rect_base.h", + "rect_base_impl.h", + "rect_conversions.cc", + "rect_conversions.h", + "rect_f.cc", + "rect_f.h", + "safe_integer_conversions.h", + "size.cc", + "size.h", + "size_base.h", + "size_conversions.cc", + "size_conversions.h", + "size_f.cc", + "size_f.h", + "vector2d.cc", + "vector2d.h", + "vector2d_conversions.cc", + "vector2d_conversions.h", + "vector2d_f.cc", + "vector2d_f.h", + "vector3d_f.cc", + "vector3d_f.h", + ] + + defines = [ "GFX_IMPLEMENTATION" ] + + deps = [ + "//base", + "//ui/gfx:gfx_export", + ] +} diff --git a/chromium/ui/gfx/box_f.cc b/chromium/ui/gfx/geometry/box_f.cc index 62e755bdc3e..674bb509c88 100644 --- a/chromium/ui/gfx/box_f.cc +++ b/chromium/ui/gfx/geometry/box_f.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ui/gfx/box_f.h" +#include "ui/gfx/geometry/box_f.h" #include <algorithm> diff --git a/chromium/ui/gfx/geometry/box_f.h b/chromium/ui/gfx/geometry/box_f.h new file mode 100644 index 00000000000..13fb9d0d47b --- /dev/null +++ b/chromium/ui/gfx/geometry/box_f.h @@ -0,0 +1,160 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_GFX_GEOMETRY_BOX_F_H_ +#define UI_GFX_GEOMETRY_BOX_F_H_ + +#include "ui/gfx/geometry/point3_f.h" +#include "ui/gfx/geometry/vector3d_f.h" + +namespace gfx { + +// A 3d version of gfx::RectF, with the positive z-axis pointed towards +// the camera. +class GFX_EXPORT BoxF { + public: + BoxF() + : width_(0.f), + height_(0.f), + depth_(0.f) {} + + BoxF(float width, float height, float depth) + : width_(width < 0 ? 0 : width), + height_(height < 0 ? 0 : height), + depth_(depth < 0 ? 0 : depth) {} + + BoxF(float x, float y, float z, float width, float height, float depth) + : origin_(x, y, z), + width_(width < 0 ? 0 : width), + height_(height < 0 ? 0 : height), + depth_(depth < 0 ? 0 : depth) {} + + BoxF(const Point3F& origin, float width, float height, float depth) + : origin_(origin), + width_(width < 0 ? 0 : width), + height_(height < 0 ? 0 : height), + depth_(depth < 0 ? 0 : depth) {} + + ~BoxF() {} + + // Scales all three axes by the given scale. + void Scale(float scale) { + Scale(scale, scale, scale); + } + + // Scales each axis by the corresponding given scale. + void Scale(float x_scale, float y_scale, float z_scale) { + origin_.Scale(x_scale, y_scale, z_scale); + set_size(width_ * x_scale, height_ * y_scale, depth_ * z_scale); + } + + // Moves the box by the specified distance in each dimension. + void operator+=(const Vector3dF& offset) { + origin_ += offset; + } + + // Returns true if the box has no interior points. + bool IsEmpty() const; + + // Computes the union of this box with the given box. The union is the + // smallest box that contains both boxes. + void Union(const BoxF& box); + + std::string ToString() const; + + float x() const { return origin_.x(); } + void set_x(float x) { origin_.set_x(x); } + + float y() const { return origin_.y(); } + void set_y(float y) { origin_.set_y(y); } + + float z() const { return origin_.z(); } + void set_z(float z) { origin_.set_z(z); } + + float width() const { return width_; } + void set_width(float width) { width_ = width < 0 ? 0 : width; } + + float height() const { return height_; } + void set_height(float height) { height_ = height < 0 ? 0 : height; } + + float depth() const { return depth_; } + void set_depth(float depth) { depth_ = depth < 0 ? 0 : depth; } + + float right() const { return x() + width(); } + float bottom() const { return y() + height(); } + float front() const { return z() + depth(); } + + void set_size(float width, float height, float depth) { + width_ = width < 0 ? 0 : width; + height_ = height < 0 ? 0 : height; + depth_ = depth < 0 ? 0 : depth; + } + + const Point3F& origin() const { return origin_; } + void set_origin(const Point3F& origin) { origin_ = origin; } + + // Expands |this| to contain the given point, if necessary. Please note, even + // if |this| is empty, after the function |this| will continue to contain its + // |origin_|. + void ExpandTo(const Point3F& point); + + // Expands |this| to contain the given box, if necessary. Please note, even + // if |this| is empty, after the function |this| will continue to contain its + // |origin_|. + void ExpandTo(const BoxF& box); + + private: + // Expands the box to contain the two given points. It is required that each + // component of |min| is less than or equal to the corresponding component in + // |max|. Precisely, what this function does is ensure that after the function + // completes, |this| contains origin_, min, max, and origin_ + (width_, + // height_, depth_), even if the box is empty. Emptiness checks are handled in + // the public function Union. + void ExpandTo(const Point3F& min, const Point3F& max); + + Point3F origin_; + float width_; + float height_; + float depth_; +}; + +GFX_EXPORT BoxF UnionBoxes(const BoxF& a, const BoxF& b); + +inline BoxF ScaleBox(const BoxF& b, + float x_scale, + float y_scale, + float z_scale) { + return BoxF(b.x() * x_scale, + b.y() * y_scale, + b.z() * z_scale, + b.width() * x_scale, + b.height() * y_scale, + b.depth() * z_scale); +} + +inline BoxF ScaleBox(const BoxF& b, float scale) { + return ScaleBox(b, scale, scale, scale); +} + +inline bool operator==(const BoxF& a, const BoxF& b) { + return a.origin() == b.origin() && a.width() == b.width() && + a.height() == b.height() && a.depth() == b.depth(); +} + +inline bool operator!=(const BoxF& a, const BoxF& b) { + return !(a == b); +} + +inline BoxF operator+(const BoxF& b, const Vector3dF& v) { + return BoxF(b.x() + v.x(), + b.y() + v.y(), + b.z() + v.z(), + b.width(), + b.height(), + b.depth()); +} + +} // namespace gfx + +#endif // UI_GFX_GEOMETRY_BOX_F_H_ diff --git a/chromium/ui/gfx/box_unittest.cc b/chromium/ui/gfx/geometry/box_unittest.cc index db894cae1cd..449c50f9a48 100644 --- a/chromium/ui/gfx/box_unittest.cc +++ b/chromium/ui/gfx/geometry/box_unittest.cc @@ -4,7 +4,7 @@ #include "base/basictypes.h" #include "testing/gtest/include/gtest/gtest.h" -#include "ui/gfx/box_f.h" +#include "ui/gfx/geometry/box_f.h" namespace gfx { diff --git a/chromium/ui/gfx/geometry/cubic_bezier.cc b/chromium/ui/gfx/geometry/cubic_bezier.cc new file mode 100644 index 00000000000..3d7e8fe9303 --- /dev/null +++ b/chromium/ui/gfx/geometry/cubic_bezier.cc @@ -0,0 +1,128 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/gfx/geometry/cubic_bezier.h" + +#include <algorithm> +#include <cmath> + +#include "base/logging.h" + +namespace gfx { + +namespace { + +static const double kBezierEpsilon = 1e-7; +static const int MAX_STEPS = 30; + +static double eval_bezier(double x1, double x2, double t) { + const double x1_times_3 = 3.0 * x1; + const double x2_times_3 = 3.0 * x2; + const double h3 = x1_times_3; + const double h1 = x1_times_3 - x2_times_3 + 1.0; + const double h2 = x2_times_3 - 6.0 * x1; + return t * (t * (t * h1 + h2) + h3); +} + +static double bezier_interp(double x1, + double y1, + double x2, + double y2, + double x) { + DCHECK_GE(1.0, x1); + DCHECK_LE(0.0, x1); + DCHECK_GE(1.0, x2); + DCHECK_LE(0.0, x2); + + x1 = std::min(std::max(x1, 0.0), 1.0); + x2 = std::min(std::max(x2, 0.0), 1.0); + x = std::min(std::max(x, 0.0), 1.0); + + // Step 1. Find the t corresponding to the given x. I.e., we want t such that + // eval_bezier(x1, x2, t) = x. There is a unique solution if x1 and x2 lie + // within (0, 1). + // + // We're just going to do bisection for now (for simplicity), but we could + // easily do some newton steps if this turns out to be a bottleneck. + double t = 0.0; + double step = 1.0; + for (int i = 0; i < MAX_STEPS; ++i, step *= 0.5) { + const double error = eval_bezier(x1, x2, t) - x; + if (std::abs(error) < kBezierEpsilon) + break; + t += error > 0.0 ? -step : step; + } + + // We should have terminated the above loop because we got close to x, not + // because we exceeded MAX_STEPS. Do a DCHECK here to confirm. + DCHECK_GT(kBezierEpsilon, std::abs(eval_bezier(x1, x2, t) - x)); + + // Step 2. Return the interpolated y values at the t we computed above. + return eval_bezier(y1, y2, t); +} + +} // namespace + +CubicBezier::CubicBezier(double x1, double y1, double x2, double y2) + : x1_(x1), + y1_(y1), + x2_(x2), + y2_(y2) { +} + +CubicBezier::~CubicBezier() { +} + +double CubicBezier::Solve(double x) const { + return bezier_interp(x1_, y1_, x2_, y2_, x); +} + +void CubicBezier::Range(double* min, double* max) const { + *min = 0; + *max = 1; + if (0 <= y1_ && y1_ < 1 && 0 <= y2_ && y2_ <= 1) + return; + + // Represent the function's derivative in the form at^2 + bt + c. + double a = 3 * (y1_ - y2_) + 1; + double b = 2 * (y2_ - 2 * y1_); + double c = y1_; + + // Check if the derivative is constant. + if (std::abs(a) < kBezierEpsilon && + std::abs(b) < kBezierEpsilon) + return; + + // Zeros of the function's derivative. + double t_1 = 0; + double t_2 = 0; + + if (std::abs(a) < kBezierEpsilon) { + // The function's derivative is linear. + t_1 = -c / b; + } else { + // The function's derivative is a quadratic. We find the zeros of this + // quadratic using the quadratic formula. + double discriminant = b * b - 4 * a * c; + if (discriminant < 0) + return; + double discriminant_sqrt = sqrt(discriminant); + t_1 = (-b + discriminant_sqrt) / (2 * a); + t_2 = (-b - discriminant_sqrt) / (2 * a); + } + + double sol_1 = 0; + double sol_2 = 0; + + if (0 < t_1 && t_1 < 1) + sol_1 = eval_bezier(y1_, y2_, t_1); + + if (0 < t_2 && t_2 < 1) + sol_2 = eval_bezier(y1_, y2_, t_2); + + *min = std::min(std::min(*min, sol_1), sol_2); + *max = std::max(std::max(*max, sol_1), sol_2); +} + +} // namespace gfx diff --git a/chromium/ui/gfx/geometry/cubic_bezier.h b/chromium/ui/gfx/geometry/cubic_bezier.h new file mode 100644 index 00000000000..645dfb7106d --- /dev/null +++ b/chromium/ui/gfx/geometry/cubic_bezier.h @@ -0,0 +1,36 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_GFX_GEOMETRY_CUBIC_BEZIER_H_ +#define UI_GFX_GEOMETRY_CUBIC_BEZIER_H_ + +#include "base/macros.h" +#include "ui/gfx/gfx_export.h" + +namespace gfx { + +class GFX_EXPORT CubicBezier { + public: + CubicBezier(double x1, double y1, double x2, double y2); + ~CubicBezier(); + + // Returns an approximation of y at the given x. + double Solve(double x) const; + + // Sets |min| and |max| to the bezier's minimum and maximium y values in the + // interval [0, 1]. + void Range(double* min, double* max) const; + + private: + double x1_; + double y1_; + double x2_; + double y2_; + + DISALLOW_ASSIGN(CubicBezier); +}; + +} // namespace gfx + +#endif // UI_GFX_GEOMETRY_CUBIC_BEZIER_H_ diff --git a/chromium/ui/gfx/geometry/cubic_bezier_unittest.cc b/chromium/ui/gfx/geometry/cubic_bezier_unittest.cc new file mode 100644 index 00000000000..4fd60e87825 --- /dev/null +++ b/chromium/ui/gfx/geometry/cubic_bezier_unittest.cc @@ -0,0 +1,140 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/gfx/geometry/cubic_bezier.h" + +#include "base/memory/scoped_ptr.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace gfx { +namespace { + +TEST(CubicBezierTest, Basic) { + CubicBezier function(0.25, 0.0, 0.75, 1.0); + + double epsilon = 0.00015; + + EXPECT_NEAR(function.Solve(0), 0, epsilon); + EXPECT_NEAR(function.Solve(0.05), 0.01136, epsilon); + EXPECT_NEAR(function.Solve(0.1), 0.03978, epsilon); + EXPECT_NEAR(function.Solve(0.15), 0.079780, epsilon); + EXPECT_NEAR(function.Solve(0.2), 0.12803, epsilon); + EXPECT_NEAR(function.Solve(0.25), 0.18235, epsilon); + EXPECT_NEAR(function.Solve(0.3), 0.24115, epsilon); + EXPECT_NEAR(function.Solve(0.35), 0.30323, epsilon); + EXPECT_NEAR(function.Solve(0.4), 0.36761, epsilon); + EXPECT_NEAR(function.Solve(0.45), 0.43345, epsilon); + EXPECT_NEAR(function.Solve(0.5), 0.5, epsilon); + EXPECT_NEAR(function.Solve(0.6), 0.63238, epsilon); + EXPECT_NEAR(function.Solve(0.65), 0.69676, epsilon); + EXPECT_NEAR(function.Solve(0.7), 0.75884, epsilon); + EXPECT_NEAR(function.Solve(0.75), 0.81764, epsilon); + EXPECT_NEAR(function.Solve(0.8), 0.87196, epsilon); + EXPECT_NEAR(function.Solve(0.85), 0.92021, epsilon); + EXPECT_NEAR(function.Solve(0.9), 0.96021, epsilon); + EXPECT_NEAR(function.Solve(0.95), 0.98863, epsilon); + EXPECT_NEAR(function.Solve(1), 1, epsilon); +} + +// Tests that solving the bezier works with knots with y not in (0, 1). +TEST(CubicBezierTest, UnclampedYValues) { + CubicBezier function(0.5, -1.0, 0.5, 2.0); + + double epsilon = 0.00015; + + EXPECT_NEAR(function.Solve(0.0), 0.0, epsilon); + EXPECT_NEAR(function.Solve(0.05), -0.08954, epsilon); + EXPECT_NEAR(function.Solve(0.1), -0.15613, epsilon); + EXPECT_NEAR(function.Solve(0.15), -0.19641, epsilon); + EXPECT_NEAR(function.Solve(0.2), -0.20651, epsilon); + EXPECT_NEAR(function.Solve(0.25), -0.18232, epsilon); + EXPECT_NEAR(function.Solve(0.3), -0.11992, epsilon); + EXPECT_NEAR(function.Solve(0.35), -0.01672, epsilon); + EXPECT_NEAR(function.Solve(0.4), 0.12660, epsilon); + EXPECT_NEAR(function.Solve(0.45), 0.30349, epsilon); + EXPECT_NEAR(function.Solve(0.5), 0.50000, epsilon); + EXPECT_NEAR(function.Solve(0.55), 0.69651, epsilon); + EXPECT_NEAR(function.Solve(0.6), 0.87340, epsilon); + EXPECT_NEAR(function.Solve(0.65), 1.01672, epsilon); + EXPECT_NEAR(function.Solve(0.7), 1.11992, epsilon); + EXPECT_NEAR(function.Solve(0.75), 1.18232, epsilon); + EXPECT_NEAR(function.Solve(0.8), 1.20651, epsilon); + EXPECT_NEAR(function.Solve(0.85), 1.19641, epsilon); + EXPECT_NEAR(function.Solve(0.9), 1.15613, epsilon); + EXPECT_NEAR(function.Solve(0.95), 1.08954, epsilon); + EXPECT_NEAR(function.Solve(1.0), 1.0, epsilon); +} + +TEST(CubicBezierTest, Range) { + double epsilon = 0.00015; + double min, max; + + // Derivative is a constant. + scoped_ptr<CubicBezier> function( + new CubicBezier(0.25, (1.0 / 3.0), 0.75, (2.0 / 3.0))); + function->Range(&min, &max); + EXPECT_EQ(0, min); + EXPECT_EQ(1, max); + + // Derivative is linear. + function.reset(new CubicBezier(0.25, -0.5, 0.75, (-1.0 / 6.0))); + function->Range(&min, &max); + EXPECT_NEAR(min, -0.225, epsilon); + EXPECT_EQ(1, max); + + // Derivative has no real roots. + function.reset(new CubicBezier(0.25, 0.25, 0.75, 0.5)); + function->Range(&min, &max); + EXPECT_EQ(0, min); + EXPECT_EQ(1, max); + + // Derivative has exactly one real root. + function.reset(new CubicBezier(0.0, 1.0, 1.0, 0.0)); + function->Range(&min, &max); + EXPECT_EQ(0, min); + EXPECT_EQ(1, max); + + // Derivative has one root < 0 and one root > 1. + function.reset(new CubicBezier(0.25, 0.1, 0.75, 0.9)); + function->Range(&min, &max); + EXPECT_EQ(0, min); + EXPECT_EQ(1, max); + + // Derivative has two roots in [0,1]. + function.reset(new CubicBezier(0.25, 2.5, 0.75, 0.5)); + function->Range(&min, &max); + EXPECT_EQ(0, min); + EXPECT_NEAR(max, 1.28818, epsilon); + function.reset(new CubicBezier(0.25, 0.5, 0.75, -1.5)); + function->Range(&min, &max); + EXPECT_NEAR(min, -0.28818, epsilon); + EXPECT_EQ(1, max); + + // Derivative has one root < 0 and one root in [0,1]. + function.reset(new CubicBezier(0.25, 0.1, 0.75, 1.5)); + function->Range(&min, &max); + EXPECT_EQ(0, min); + EXPECT_NEAR(max, 1.10755, epsilon); + + // Derivative has one root in [0,1] and one root > 1. + function.reset(new CubicBezier(0.25, -0.5, 0.75, 0.9)); + function->Range(&min, &max); + EXPECT_NEAR(min, -0.10755, epsilon); + EXPECT_EQ(1, max); + + // Derivative has two roots < 0. + function.reset(new CubicBezier(0.25, 0.3, 0.75, 0.633)); + function->Range(&min, &max); + EXPECT_EQ(0, min); + EXPECT_EQ(1, max); + + // Derivative has two roots > 1. + function.reset(new CubicBezier(0.25, 0.367, 0.75, 0.7)); + function->Range(&min, &max); + EXPECT_EQ(0.f, min); + EXPECT_EQ(1.f, max); +} + +} // namespace +} // namespace gfx diff --git a/chromium/ui/gfx/insets.cc b/chromium/ui/gfx/geometry/insets.cc index 44a29916acf..c29368e1bd9 100644 --- a/chromium/ui/gfx/insets.cc +++ b/chromium/ui/gfx/geometry/insets.cc @@ -2,11 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ui/gfx/insets.h" - -#if defined(TOOLKIT_GTK) -#include <gtk/gtk.h> -#endif +#include "ui/gfx/geometry/insets.h" #include "base/strings/stringprintf.h" @@ -19,15 +15,6 @@ Insets::Insets() : InsetsBase<Insets, int>(0, 0, 0, 0) {} Insets::Insets(int top, int left, int bottom, int right) : InsetsBase<Insets, int>(top, left, bottom, right) {} -#if defined(TOOLKIT_GTK) -Insets::Insets(const GtkBorder& border) - : InsetsBase<Insets, int>(border.top, - border.left, - border.bottom, - border.right) { -} -#endif - Insets::~Insets() {} std::string Insets::ToString() const { diff --git a/chromium/ui/gfx/geometry/insets.h b/chromium/ui/gfx/geometry/insets.h new file mode 100644 index 00000000000..00b612540dd --- /dev/null +++ b/chromium/ui/gfx/geometry/insets.h @@ -0,0 +1,50 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_GFX_GEOMETRY_INSETS_H_ +#define UI_GFX_GEOMETRY_INSETS_H_ + +#include <string> + +#include "build/build_config.h" +#include "ui/gfx/geometry/insets_base.h" +#include "ui/gfx/geometry/insets_f.h" +#include "ui/gfx/gfx_export.h" + +namespace gfx { + +// An integer version of gfx::Insets. +class GFX_EXPORT Insets : public InsetsBase<Insets, int> { + public: + Insets(); + Insets(int top, int left, int bottom, int right); + + ~Insets(); + + Insets Scale(float scale) const { + return Scale(scale, scale); + } + + Insets Scale(float x_scale, float y_scale) const { + return Insets(static_cast<int>(top() * y_scale), + static_cast<int>(left() * x_scale), + static_cast<int>(bottom() * y_scale), + static_cast<int>(right() * x_scale)); + } + + operator InsetsF() const { + return InsetsF(top(), left(), bottom(), right()); + } + + // Returns a string representation of the insets. + std::string ToString() const; +}; + +#if !defined(COMPILER_MSVC) +extern template class InsetsBase<Insets, int>; +#endif + +} // namespace gfx + +#endif // UI_GFX_GEOMETRY_INSETS_H_ diff --git a/chromium/ui/gfx/insets_base.h b/chromium/ui/gfx/geometry/insets_base.h index da7aca103e5..751d5c11d9b 100644 --- a/chromium/ui/gfx/insets_base.h +++ b/chromium/ui/gfx/geometry/insets_base.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_GFX_INSETS_BASE_H_ -#define UI_GFX_INSETS_BASE_H_ +#ifndef UI_GFX_GEOMETRY_INSETS_BASE_H_ +#define UI_GFX_GEOMETRY_INSETS_BASE_H_ #include "ui/gfx/gfx_export.h" @@ -77,4 +77,4 @@ class GFX_EXPORT InsetsBase { } // namespace gfx -#endif // UI_GFX_INSETS_BASE_H_ +#endif // UI_GFX_GEOMETRY_INSETS_BASE_H_ diff --git a/chromium/ui/gfx/insets_f.cc b/chromium/ui/gfx/geometry/insets_f.cc index f790b3c4187..774b4ab8ca8 100644 --- a/chromium/ui/gfx/insets_f.cc +++ b/chromium/ui/gfx/geometry/insets_f.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ui/gfx/insets_f.h" +#include "ui/gfx/geometry/insets_f.h" #include "base/strings/stringprintf.h" diff --git a/chromium/ui/gfx/geometry/insets_f.h b/chromium/ui/gfx/geometry/insets_f.h new file mode 100644 index 00000000000..ac3585a5a59 --- /dev/null +++ b/chromium/ui/gfx/geometry/insets_f.h @@ -0,0 +1,33 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_GFX_GEOMETRY_INSETS_F_H_ +#define UI_GFX_GEOMETRY_INSETS_F_H_ + +#include <string> + +#include "build/build_config.h" +#include "ui/gfx/geometry/insets_base.h" +#include "ui/gfx/gfx_export.h" + +namespace gfx { + +// A floating versin of gfx::Insets. +class GFX_EXPORT InsetsF : public InsetsBase<InsetsF, float> { + public: + InsetsF(); + InsetsF(float top, float left, float bottom, float right); + ~InsetsF(); + + // Returns a string representation of the insets. + std::string ToString() const; +}; + +#if !defined(COMPILER_MSVC) +extern template class InsetsBase<InsetsF, float>; +#endif + +} // namespace gfx + +#endif // UI_GFX_GEOMETRY_INSETS_F_H_ diff --git a/chromium/ui/gfx/insets_unittest.cc b/chromium/ui/gfx/geometry/insets_unittest.cc index 563f20fca23..b20a9d36f48 100644 --- a/chromium/ui/gfx/insets_unittest.cc +++ b/chromium/ui/gfx/geometry/insets_unittest.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ui/gfx/insets.h" +#include "ui/gfx/geometry/insets.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/chromium/ui/gfx/matrix3_f.cc b/chromium/ui/gfx/geometry/matrix3_f.cc index 562fdb3a964..5836ae677b9 100644 --- a/chromium/ui/gfx/matrix3_f.cc +++ b/chromium/ui/gfx/geometry/matrix3_f.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ui/gfx/matrix3_f.h" +#include "ui/gfx/geometry/matrix3_f.h" #include <algorithm> #include <cmath> diff --git a/chromium/ui/gfx/geometry/matrix3_f.h b/chromium/ui/gfx/geometry/matrix3_f.h new file mode 100644 index 00000000000..7e43496fd71 --- /dev/null +++ b/chromium/ui/gfx/geometry/matrix3_f.h @@ -0,0 +1,108 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_GFX_GEOMETRY_MATRIX3_F_H_ +#define UI_GFX_GEOMETRY_MATRIX3_F_H_ + +#include "base/logging.h" +#include "ui/gfx/geometry/vector3d_f.h" + +namespace gfx { + +class GFX_EXPORT Matrix3F { + public: + ~Matrix3F(); + + static Matrix3F Zeros(); + static Matrix3F Ones(); + static Matrix3F Identity(); + static Matrix3F FromOuterProduct(const Vector3dF& a, const Vector3dF& bt); + + bool IsEqual(const Matrix3F& rhs) const; + + // Element-wise comparison with given precision. + bool IsNear(const Matrix3F& rhs, float precision) const; + + float get(int i, int j) const { + return data_[MatrixToArrayCoords(i, j)]; + } + + void set(int i, int j, float v) { + data_[MatrixToArrayCoords(i, j)] = v; + } + + void set(float m00, float m01, float m02, + float m10, float m11, float m12, + float m20, float m21, float m22) { + data_[0] = m00; + data_[1] = m01; + data_[2] = m02; + data_[3] = m10; + data_[4] = m11; + data_[5] = m12; + data_[6] = m20; + data_[7] = m21; + data_[8] = m22; + } + + Vector3dF get_column(int i) const { + return Vector3dF( + data_[MatrixToArrayCoords(0, i)], + data_[MatrixToArrayCoords(1, i)], + data_[MatrixToArrayCoords(2, i)]); + } + + void set_column(int i, const Vector3dF& c) { + data_[MatrixToArrayCoords(0, i)] = c.x(); + data_[MatrixToArrayCoords(1, i)] = c.y(); + data_[MatrixToArrayCoords(2, i)] = c.z(); + } + + // Returns an inverse of this if the matrix is non-singular, zero (== Zero()) + // otherwise. + Matrix3F Inverse() const; + + // Value of the determinant of the matrix. + float Determinant() const; + + // Trace (sum of diagonal elements) of the matrix. + float Trace() const { + return data_[MatrixToArrayCoords(0, 0)] + + data_[MatrixToArrayCoords(1, 1)] + + data_[MatrixToArrayCoords(2, 2)]; + } + + // Compute eigenvalues and (optionally) normalized eigenvectors of + // a positive defnite matrix *this. Eigenvectors are computed only if + // non-null |eigenvectors| matrix is passed. If it is NULL, the routine + // will not attempt to compute eigenvectors but will still return eigenvalues + // if they can be computed. + // If eigenvalues cannot be computed (the matrix does not meet constraints) + // the 0-vector is returned. Note that to retrieve eigenvalues, the matrix + // only needs to be symmetric while eigenvectors require it to be + // positive-definite. Passing a non-positive definite matrix will result in + // NaNs in vectors which cannot be computed. + // Eigenvectors are placed as column in |eigenvectors| in order corresponding + // to eigenvalues. + Vector3dF SolveEigenproblem(Matrix3F* eigenvectors) const; + + private: + Matrix3F(); // Uninitialized default. + + static int MatrixToArrayCoords(int i, int j) { + DCHECK(i >= 0 && i < 3); + DCHECK(j >= 0 && j < 3); + return i * 3 + j; + } + + float data_[9]; +}; + +inline bool operator==(const Matrix3F& lhs, const Matrix3F& rhs) { + return lhs.IsEqual(rhs); +} + +} // namespace gfx + +#endif // UI_GFX_GEOMETRY_MATRIX3_F_H_ diff --git a/chromium/ui/gfx/matrix3_unittest.cc b/chromium/ui/gfx/geometry/matrix3_unittest.cc index 04c656f6d26..0f57e8e7a95 100644 --- a/chromium/ui/gfx/matrix3_unittest.cc +++ b/chromium/ui/gfx/geometry/matrix3_unittest.cc @@ -7,7 +7,7 @@ #include "base/basictypes.h" #include "testing/gtest/include/gtest/gtest.h" -#include "ui/gfx/matrix3_f.h" +#include "ui/gfx/geometry/matrix3_f.h" namespace gfx { namespace { diff --git a/chromium/ui/gfx/point.cc b/chromium/ui/gfx/geometry/point.cc index 7fdf3560fe3..5a68ba67bdd 100644 --- a/chromium/ui/gfx/point.cc +++ b/chromium/ui/gfx/geometry/point.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ui/gfx/point.h" +#include "ui/gfx/geometry/point.h" #if defined(OS_WIN) #include <windows.h> diff --git a/chromium/ui/gfx/geometry/point.h b/chromium/ui/gfx/geometry/point.h new file mode 100644 index 00000000000..ff99d71d361 --- /dev/null +++ b/chromium/ui/gfx/geometry/point.h @@ -0,0 +1,90 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_GFX_GEOMETRY_POINT_H_ +#define UI_GFX_GEOMETRY_POINT_H_ + +#include "ui/gfx/geometry/point_base.h" +#include "ui/gfx/geometry/point_f.h" +#include "ui/gfx/geometry/vector2d.h" +#include "ui/gfx/gfx_export.h" + +#if defined(OS_WIN) +typedef unsigned long DWORD; +typedef struct tagPOINT POINT; +#elif defined(OS_IOS) +#include <CoreGraphics/CoreGraphics.h> +#elif defined(OS_MACOSX) +#include <ApplicationServices/ApplicationServices.h> +#endif + +namespace gfx { + +// A point has an x and y coordinate. +class GFX_EXPORT Point : public PointBase<Point, int, Vector2d> { + public: + Point() : PointBase<Point, int, Vector2d>(0, 0) {} + Point(int x, int y) : PointBase<Point, int, Vector2d>(x, y) {} +#if defined(OS_WIN) + // |point| is a DWORD value that contains a coordinate. The x-coordinate is + // the low-order short and the y-coordinate is the high-order short. This + // value is commonly acquired from GetMessagePos/GetCursorPos. + explicit Point(DWORD point); + explicit Point(const POINT& point); + Point& operator=(const POINT& point); +#elif defined(OS_MACOSX) + explicit Point(const CGPoint& point); +#endif + + ~Point() {} + +#if defined(OS_WIN) + POINT ToPOINT() const; +#elif defined(OS_MACOSX) + CGPoint ToCGPoint() const; +#endif + + operator PointF() const { + return PointF(x(), y()); + } + + // Returns a string representation of point. + std::string ToString() const; +}; + +inline bool operator==(const Point& lhs, const Point& rhs) { + return lhs.x() == rhs.x() && lhs.y() == rhs.y(); +} + +inline bool operator!=(const Point& lhs, const Point& rhs) { + return !(lhs == rhs); +} + +inline Point operator+(const Point& lhs, const Vector2d& rhs) { + Point result(lhs); + result += rhs; + return result; +} + +inline Point operator-(const Point& lhs, const Vector2d& rhs) { + Point result(lhs); + result -= rhs; + return result; +} + +inline Vector2d operator-(const Point& lhs, const Point& rhs) { + return Vector2d(lhs.x() - rhs.x(), lhs.y() - rhs.y()); +} + +inline Point PointAtOffsetFromOrigin(const Vector2d& offset_from_origin) { + return Point(offset_from_origin.x(), offset_from_origin.y()); +} + +#if !defined(COMPILER_MSVC) +extern template class PointBase<Point, int, Vector2d>; +#endif + +} // namespace gfx + +#endif // UI_GFX_GEOMETRY_POINT_H_ diff --git a/chromium/ui/gfx/point3_f.cc b/chromium/ui/gfx/geometry/point3_f.cc index 70089a46c21..465376e55e8 100644 --- a/chromium/ui/gfx/point3_f.cc +++ b/chromium/ui/gfx/geometry/point3_f.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ui/gfx/point3_f.h" +#include "ui/gfx/geometry/point3_f.h" #include "base/strings/stringprintf.h" diff --git a/chromium/ui/gfx/geometry/point3_f.h b/chromium/ui/gfx/geometry/point3_f.h new file mode 100644 index 00000000000..45bae6e8d32 --- /dev/null +++ b/chromium/ui/gfx/geometry/point3_f.h @@ -0,0 +1,120 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_GFX_GEOMETRY_POINT3_F_H_ +#define UI_GFX_GEOMETRY_POINT3_F_H_ + +#include <string> + +#include "ui/gfx/geometry/point_f.h" +#include "ui/gfx/geometry/vector3d_f.h" +#include "ui/gfx/gfx_export.h" + +namespace gfx { + +// A point has an x, y and z coordinate. +class GFX_EXPORT Point3F { + public: + Point3F() : x_(0), y_(0), z_(0) {} + + Point3F(float x, float y, float z) : x_(x), y_(y), z_(z) {} + + explicit Point3F(const PointF& point) : x_(point.x()), y_(point.y()), z_(0) {} + + ~Point3F() {} + + void Scale(float scale) { + Scale(scale, scale, scale); + } + + void Scale(float x_scale, float y_scale, float z_scale) { + SetPoint(x() * x_scale, y() * y_scale, z() * z_scale); + } + + float x() const { return x_; } + float y() const { return y_; } + float z() const { return z_; } + + void set_x(float x) { x_ = x; } + void set_y(float y) { y_ = y; } + void set_z(float z) { z_ = z; } + + void SetPoint(float x, float y, float z) { + x_ = x; + y_ = y; + z_ = z; + } + + // Offset the point by the given vector. + void operator+=(const Vector3dF& v) { + x_ += v.x(); + y_ += v.y(); + z_ += v.z(); + } + + // Offset the point by the given vector's inverse. + void operator-=(const Vector3dF& v) { + x_ -= v.x(); + y_ -= v.y(); + z_ -= v.z(); + } + + // Returns the squared euclidean distance between two points. + float SquaredDistanceTo(const Point3F& other) const { + float dx = x_ - other.x_; + float dy = y_ - other.y_; + float dz = z_ - other.z_; + return dx * dx + dy * dy + dz * dz; + } + + PointF AsPointF() const { return PointF(x_, y_); } + + // Returns a string representation of 3d point. + std::string ToString() const; + + private: + float x_; + float y_; + float z_; + + // copy/assign are allowed. +}; + +inline bool operator==(const Point3F& lhs, const Point3F& rhs) { + return lhs.x() == rhs.x() && lhs.y() == rhs.y() && lhs.z() == rhs.z(); +} + +inline bool operator!=(const Point3F& lhs, const Point3F& rhs) { + return !(lhs == rhs); +} + +// Add a vector to a point, producing a new point offset by the vector. +GFX_EXPORT Point3F operator+(const Point3F& lhs, const Vector3dF& rhs); + +// Subtract a vector from a point, producing a new point offset by the vector's +// inverse. +GFX_EXPORT Point3F operator-(const Point3F& lhs, const Vector3dF& rhs); + +// Subtract one point from another, producing a vector that represents the +// distances between the two points along each axis. +GFX_EXPORT Vector3dF operator-(const Point3F& lhs, const Point3F& rhs); + +inline Point3F PointAtOffsetFromOrigin(const Vector3dF& offset) { + return Point3F(offset.x(), offset.y(), offset.z()); +} + +inline Point3F ScalePoint(const Point3F& p, + float x_scale, + float y_scale, + float z_scale) { + return Point3F(p.x() * x_scale, p.y() * y_scale, p.z() * z_scale); +} + +inline Point3F ScalePoint(const Point3F& p, float scale) { + return ScalePoint(p, scale, scale, scale); +} + +} // namespace gfx + +#endif // UI_GFX_GEOMETRY_POINT3_F_H_ diff --git a/chromium/ui/gfx/point3_unittest.cc b/chromium/ui/gfx/geometry/point3_unittest.cc index 735ffd55425..77f20d79963 100644 --- a/chromium/ui/gfx/point3_unittest.cc +++ b/chromium/ui/gfx/geometry/point3_unittest.cc @@ -2,10 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ui/gfx/point3_f.h" - #include "base/basictypes.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/gfx/geometry/point3_f.h" namespace gfx { diff --git a/chromium/ui/gfx/point_base.h b/chromium/ui/gfx/geometry/point_base.h index d7a3951913e..6db1c0cf916 100644 --- a/chromium/ui/gfx/point_base.h +++ b/chromium/ui/gfx/geometry/point_base.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_GFX_POINT_BASE_H_ -#define UI_GFX_POINT_BASE_H_ +#ifndef UI_GFX_GEOMETRY_POINT_BASE_H_ +#define UI_GFX_GEOMETRY_POINT_BASE_H_ #include <string> @@ -84,4 +84,4 @@ class GFX_EXPORT PointBase { } // namespace gfx -#endif // UI_GFX_POINT_BASE_H_ +#endif // UI_GFX_GEOMETRY_POINT_BASE_H_ diff --git a/chromium/ui/gfx/point_conversions.cc b/chromium/ui/gfx/geometry/point_conversions.cc index f7845a03f41..0613e7a9db1 100644 --- a/chromium/ui/gfx/point_conversions.cc +++ b/chromium/ui/gfx/geometry/point_conversions.cc @@ -2,9 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ui/gfx/point_conversions.h" +#include "ui/gfx/geometry/point_conversions.h" -#include "ui/gfx/safe_integer_conversions.h" +#include "ui/gfx/geometry/safe_integer_conversions.h" namespace gfx { diff --git a/chromium/ui/gfx/geometry/point_conversions.h b/chromium/ui/gfx/geometry/point_conversions.h new file mode 100644 index 00000000000..bfab9e4e463 --- /dev/null +++ b/chromium/ui/gfx/geometry/point_conversions.h @@ -0,0 +1,24 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_GFX_GEOMETRY_POINT_CONVERSIONS_H_ +#define UI_GFX_GEOMETRY_POINT_CONVERSIONS_H_ + +#include "ui/gfx/geometry/point.h" +#include "ui/gfx/geometry/point_f.h" + +namespace gfx { + +// Returns a Point with each component from the input PointF floored. +GFX_EXPORT Point ToFlooredPoint(const PointF& point); + +// Returns a Point with each component from the input PointF ceiled. +GFX_EXPORT Point ToCeiledPoint(const PointF& point); + +// Returns a Point with each component from the input PointF rounded. +GFX_EXPORT Point ToRoundedPoint(const PointF& point); + +} // namespace gfx + +#endif // UI_GFX_GEOMETRY_POINT_CONVERSIONS_H_ diff --git a/chromium/ui/gfx/point_f.cc b/chromium/ui/gfx/geometry/point_f.cc index 21028565d99..9c2ece23a59 100644 --- a/chromium/ui/gfx/point_f.cc +++ b/chromium/ui/gfx/geometry/point_f.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ui/gfx/point_f.h" +#include "ui/gfx/geometry/point_f.h" #include "base/strings/stringprintf.h" diff --git a/chromium/ui/gfx/geometry/point_f.h b/chromium/ui/gfx/geometry/point_f.h new file mode 100644 index 00000000000..00266d311d0 --- /dev/null +++ b/chromium/ui/gfx/geometry/point_f.h @@ -0,0 +1,75 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_GFX_GEOMETRY_POINT_F_H_ +#define UI_GFX_GEOMETRY_POINT_F_H_ + +#include <string> + +#include "ui/gfx/geometry/point_base.h" +#include "ui/gfx/geometry/vector2d_f.h" +#include "ui/gfx/gfx_export.h" + +namespace gfx { + +// A floating version of gfx::Point. +class GFX_EXPORT PointF : public PointBase<PointF, float, Vector2dF> { + public: + PointF() : PointBase<PointF, float, Vector2dF>(0, 0) {} + PointF(float x, float y) : PointBase<PointF, float, Vector2dF>(x, y) {} + ~PointF() {} + + void Scale(float scale) { + Scale(scale, scale); + } + + void Scale(float x_scale, float y_scale) { + SetPoint(x() * x_scale, y() * y_scale); + } + + // Returns a string representation of point. + std::string ToString() const; +}; + +inline bool operator==(const PointF& lhs, const PointF& rhs) { + return lhs.x() == rhs.x() && lhs.y() == rhs.y(); +} + +inline bool operator!=(const PointF& lhs, const PointF& rhs) { + return !(lhs == rhs); +} + +inline PointF operator+(const PointF& lhs, const Vector2dF& rhs) { + PointF result(lhs); + result += rhs; + return result; +} + +inline PointF operator-(const PointF& lhs, const Vector2dF& rhs) { + PointF result(lhs); + result -= rhs; + return result; +} + +inline Vector2dF operator-(const PointF& lhs, const PointF& rhs) { + return Vector2dF(lhs.x() - rhs.x(), lhs.y() - rhs.y()); +} + +inline PointF PointAtOffsetFromOrigin(const Vector2dF& offset_from_origin) { + return PointF(offset_from_origin.x(), offset_from_origin.y()); +} + +GFX_EXPORT PointF ScalePoint(const PointF& p, float x_scale, float y_scale); + +inline PointF ScalePoint(const PointF& p, float scale) { + return ScalePoint(p, scale, scale); +} + +#if !defined(COMPILER_MSVC) +extern template class PointBase<PointF, float, Vector2dF>; +#endif + +} // namespace gfx + +#endif // UI_GFX_GEOMETRY_POINT_F_H_ diff --git a/chromium/ui/gfx/point_unittest.cc b/chromium/ui/gfx/geometry/point_unittest.cc index 6cf73dd2adb..8c5f552f89c 100644 --- a/chromium/ui/gfx/point_unittest.cc +++ b/chromium/ui/gfx/geometry/point_unittest.cc @@ -2,13 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ui/gfx/point_base.h" - #include "base/basictypes.h" #include "testing/gtest/include/gtest/gtest.h" -#include "ui/gfx/point.h" -#include "ui/gfx/point_conversions.h" -#include "ui/gfx/point_f.h" +#include "ui/gfx/geometry/point.h" +#include "ui/gfx/geometry/point_base.h" +#include "ui/gfx/geometry/point_conversions.h" +#include "ui/gfx/geometry/point_f.h" namespace gfx { diff --git a/chromium/ui/gfx/quad_f.cc b/chromium/ui/gfx/geometry/quad_f.cc index 2796bf192b2..dbc50458b3f 100644 --- a/chromium/ui/gfx/quad_f.cc +++ b/chromium/ui/gfx/geometry/quad_f.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ui/gfx/quad_f.h" +#include "ui/gfx/geometry/quad_f.h" #include <limits> @@ -62,28 +62,24 @@ static inline bool PointIsInTriangle(const PointF& point, const PointF& r1, const PointF& r2, const PointF& r3) { - // Compute the barycentric coordinates of |point| relative to the triangle - // (r1, r2, r3). This algorithm comes from Christer Ericson's Real-Time - // Collision Detection. - Vector2dF v0 = r2 - r1; - Vector2dF v1 = r3 - r1; - Vector2dF v2 = point - r1; + // Compute the barycentric coordinates (u, v, w) of |point| relative to the + // triangle (r1, r2, r3) by the solving the system of equations: + // 1) point = u * r1 + v * r2 + w * r3 + // 2) u + v + w = 1 + // This algorithm comes from Christer Ericson's Real-Time Collision Detection. - double dot00 = DotProduct(v0, v0); - double dot01 = DotProduct(v0, v1); - double dot11 = DotProduct(v1, v1); - double dot20 = DotProduct(v2, v0); - double dot21 = DotProduct(v2, v1); + Vector2dF r31 = r1 - r3; + Vector2dF r32 = r2 - r3; + Vector2dF r3p = point - r3; - double denom = dot00 * dot11 - dot01 * dot01; - - double v = (dot11 * dot20 - dot01 * dot21) / denom; - double w = (dot00 * dot21 - dot01 * dot20) / denom; - double u = 1 - v - w; + float denom = r32.y() * r31.x() - r32.x() * r31.y(); + float u = (r32.y() * r3p.x() - r32.x() * r3p.y()) / denom; + float v = (r31.x() * r3p.y() - r31.y() * r3p.x()) / denom; + float w = 1.f - u - v; // Use the barycentric coordinates to test if |point| is inside the // triangle (r1, r2, r2). - return (v >= 0) && (w >= 0) && (u >= 0); + return (u >= 0) && (v >= 0) && (w >= 0); } bool QuadF::Contains(const PointF& point) const { diff --git a/chromium/ui/gfx/geometry/quad_f.h b/chromium/ui/gfx/geometry/quad_f.h new file mode 100644 index 00000000000..25f8b2b34b3 --- /dev/null +++ b/chromium/ui/gfx/geometry/quad_f.h @@ -0,0 +1,109 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_GFX_GEOMETRY_QUAD_F_H_ +#define UI_GFX_GEOMETRY_QUAD_F_H_ + +#include <algorithm> +#include <cmath> +#include <string> + +#include "ui/gfx/geometry/point_f.h" +#include "ui/gfx/geometry/rect_f.h" +#include "ui/gfx/gfx_export.h" + +namespace gfx { + +// A Quad is defined by four corners, allowing it to have edges that are not +// axis-aligned, unlike a Rect. +class GFX_EXPORT QuadF { + public: + QuadF() {} + QuadF(const PointF& p1, const PointF& p2, const PointF& p3, const PointF& p4) + : p1_(p1), + p2_(p2), + p3_(p3), + p4_(p4) {} + + explicit QuadF(const RectF& rect) + : p1_(rect.x(), rect.y()), + p2_(rect.right(), rect.y()), + p3_(rect.right(), rect.bottom()), + p4_(rect.x(), rect.bottom()) {} + + void operator=(const RectF& rect); + + void set_p1(const PointF& p) { p1_ = p; } + void set_p2(const PointF& p) { p2_ = p; } + void set_p3(const PointF& p) { p3_ = p; } + void set_p4(const PointF& p) { p4_ = p; } + + const PointF& p1() const { return p1_; } + const PointF& p2() const { return p2_; } + const PointF& p3() const { return p3_; } + const PointF& p4() const { return p4_; } + + // Returns true if the quad is an axis-aligned rectangle. + bool IsRectilinear() const; + + // Returns true if the points of the quad are in counter-clockwise order. This + // assumes that the quad is convex, and that no three points are collinear. + bool IsCounterClockwise() const; + + // Returns true if the |point| is contained within the quad, or lies on on + // edge of the quad. This assumes that the quad is convex. + bool Contains(const gfx::PointF& point) const; + + // Returns a rectangle that bounds the four points of the quad. The points of + // the quad may lie on the right/bottom edge of the resulting rectangle, + // rather than being strictly inside it. + RectF BoundingBox() const { + float rl = std::min(std::min(p1_.x(), p2_.x()), std::min(p3_.x(), p4_.x())); + float rr = std::max(std::max(p1_.x(), p2_.x()), std::max(p3_.x(), p4_.x())); + float rt = std::min(std::min(p1_.y(), p2_.y()), std::min(p3_.y(), p4_.y())); + float rb = std::max(std::max(p1_.y(), p2_.y()), std::max(p3_.y(), p4_.y())); + return RectF(rl, rt, rr - rl, rb - rt); + } + + // Add a vector to the quad, offseting each point in the quad by the vector. + void operator+=(const Vector2dF& rhs); + // Subtract a vector from the quad, offseting each point in the quad by the + // inverse of the vector. + void operator-=(const Vector2dF& rhs); + + // Scale each point in the quad by the |scale| factor. + void Scale(float scale) { Scale(scale, scale); } + + // Scale each point in the quad by the scale factors along each axis. + void Scale(float x_scale, float y_scale); + + // Returns a string representation of quad. + std::string ToString() const; + + private: + PointF p1_; + PointF p2_; + PointF p3_; + PointF p4_; +}; + +inline bool operator==(const QuadF& lhs, const QuadF& rhs) { + return + lhs.p1() == rhs.p1() && lhs.p2() == rhs.p2() && + lhs.p3() == rhs.p3() && lhs.p4() == rhs.p4(); +} + +inline bool operator!=(const QuadF& lhs, const QuadF& rhs) { + return !(lhs == rhs); +} + +// Add a vector to a quad, offseting each point in the quad by the vector. +GFX_EXPORT QuadF operator+(const QuadF& lhs, const Vector2dF& rhs); +// Subtract a vector from a quad, offseting each point in the quad by the +// inverse of the vector. +GFX_EXPORT QuadF operator-(const QuadF& lhs, const Vector2dF& rhs); + +} // namespace gfx + +#endif // UI_GFX_GEOMETRY_QUAD_F_H_ diff --git a/chromium/ui/gfx/quad_unittest.cc b/chromium/ui/gfx/geometry/quad_unittest.cc index 8859a0e6972..6ba2b51813e 100644 --- a/chromium/ui/gfx/quad_unittest.cc +++ b/chromium/ui/gfx/geometry/quad_unittest.cc @@ -2,11 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ui/gfx/quad_f.h" - #include "base/basictypes.h" #include "testing/gtest/include/gtest/gtest.h" -#include "ui/gfx/rect_f.h" +#include "ui/gfx/geometry/quad_f.h" +#include "ui/gfx/geometry/rect_f.h" namespace gfx { diff --git a/chromium/ui/gfx/geometry/r_tree.h b/chromium/ui/gfx/geometry/r_tree.h new file mode 100644 index 00000000000..53fd5c9f900 --- /dev/null +++ b/chromium/ui/gfx/geometry/r_tree.h @@ -0,0 +1,182 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Defines a hierarchical bounding rectangle data structure for Rect objects, +// associated with a generic unique key K for efficient spatial queries. The +// R*-tree algorithm is used to build the trees. Based on the papers: +// +// A Guttman 'R-trees: a dynamic index structure for spatial searching', Proc +// ACM SIGMOD Int Conf on Management of Data, 47-57, 1984 +// +// N Beckmann, H-P Kriegel, R Schneider, B Seeger 'The R*-tree: an efficient and +// robust access method for points and rectangles', Proc ACM SIGMOD Int Conf on +// Management of Data, 322-331, 1990 + +#ifndef UI_GFX_GEOMETRY_R_TREE_H_ +#define UI_GFX_GEOMETRY_R_TREE_H_ + +#include "r_tree_base.h" + +namespace gfx { + +template <typename Key> +class RTree : public RTreeBase { + public: + typedef base::hash_set<Key> Matches; + + // RTrees organize pairs of keys and rectangles in a hierarchical bounding + // box structure. This allows for queries of the tree within logarithmic time. + // |min_children| and |max_children| allows for adjustment of the average size + // of the nodes within RTree, which adjusts the base of the logarithm in the + // algorithm runtime. Some parts of insertion and deletion are polynomial + // in the size of the individual node, so the trade-off with larger nodes is + // potentially faster queries but slower insertions and deletions. Generally + // it is worth considering how much overlap between rectangles of different + // keys will occur in the tree, and trying to set |max_children| as a + // reasonable upper bound to the number of overlapping rectangles expected. + // Then |min_children| can bet set to a quantity slightly less than half of + // that. + RTree(size_t min_children, size_t max_children); + ~RTree(); + + // Insert a new rect into the tree, associated with provided key. Note that if + // |rect| is empty, the |key| will not actually be inserted. Duplicate keys + // overwrite old entries. + void Insert(const Rect& rect, Key key); + + // If present, remove the supplied |key| from the tree. + void Remove(Key key); + + // Fills |matches_out| with all keys having bounding rects intersecting + // |query_rect|. + void AppendIntersectingRecords(const Rect& query_rect, + Matches* matches_out) const; + + void Clear(); + + private: + friend class RTreeTest; + friend class RTreeNodeTest; + + class Record : public RecordBase { + public: + Record(const Rect& rect, const Key& key); + virtual ~Record(); + const Key& key() const { return key_; } + + private: + Key key_; + + DISALLOW_COPY_AND_ASSIGN(Record); + }; + + // A map of supplied keys to their Node representation within the RTree, for + // efficient retrieval of keys without requiring a bounding rect. + typedef base::hash_map<Key, Record*> RecordMap; + RecordMap record_map_; + + DISALLOW_COPY_AND_ASSIGN(RTree); +}; + +template <typename Key> +RTree<Key>::RTree(size_t min_children, size_t max_children) + : RTreeBase(min_children, max_children) { +} + +template <typename Key> +RTree<Key>::~RTree() { +} + +template <typename Key> +void RTree<Key>::Insert(const Rect& rect, Key key) { + scoped_ptr<NodeBase> record; + // Check if this key is already present in the tree. + typename RecordMap::iterator it(record_map_.find(key)); + + if (it != record_map_.end()) { + // We will re-use this node structure, regardless of re-insert or return. + Record* existing_record = it->second; + // If the new rect and the current rect are identical we can skip the rest + // of Insert() as nothing has changed. + if (existing_record->rect() == rect) + return; + + // Remove the node from the tree in its current position. + record = RemoveNode(existing_record); + + PruneRootIfNecessary(); + + // If we are replacing this key with an empty rectangle we just remove the + // old node from the list and return, thus preventing insertion of empty + // rectangles into our spatial database. + if (rect.IsEmpty()) { + record_map_.erase(it); + return; + } + + // Reset the rectangle to the new value. + record->set_rect(rect); + } else { + if (rect.IsEmpty()) + return; + + record.reset(new Record(rect, key)); + record_map_.insert(std::make_pair(key, static_cast<Record*>(record.get()))); + } + + int highest_reinsert_level = -1; + InsertNode(record.Pass(), &highest_reinsert_level); +} + +template <typename Key> +void RTree<Key>::Clear() { + record_map_.clear(); + ResetRoot(); +} + +template <typename Key> +void RTree<Key>::Remove(Key key) { + // Search the map for the leaf parent that has the provided record. + typename RecordMap::iterator it = record_map_.find(key); + if (it == record_map_.end()) + return; + + Record* record = it->second; + record_map_.erase(it); + RemoveNode(record); + + // Lastly check the root. If it has only one non-leaf child, delete it and + // replace it with its child. + PruneRootIfNecessary(); +} + +template <typename Key> +void RTree<Key>::AppendIntersectingRecords( + const Rect& query_rect, Matches* matches_out) const { + RTreeBase::Records matching_records; + root()->AppendIntersectingRecords(query_rect, &matching_records); + for (RTreeBase::Records::const_iterator it = matching_records.begin(); + it != matching_records.end(); + ++it) { + const Record* record = static_cast<const Record*>(*it); + matches_out->insert(record->key()); + } +} + + +// RTree::Record -------------------------------------------------------------- + +template <typename Key> +RTree<Key>::Record::Record(const Rect& rect, const Key& key) + : RecordBase(rect), + key_(key) { +} + +template <typename Key> +RTree<Key>::Record::~Record() { +} + +} // namespace gfx + +#endif // UI_GFX_GEOMETRY_R_TREE_H_ diff --git a/chromium/ui/gfx/geometry/r_tree_base.cc b/chromium/ui/gfx/geometry/r_tree_base.cc new file mode 100644 index 00000000000..e93e1d5b6b1 --- /dev/null +++ b/chromium/ui/gfx/geometry/r_tree_base.cc @@ -0,0 +1,658 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/gfx/geometry/r_tree_base.h" + +#include <algorithm> + +#include "base/logging.h" + + +// Helpers -------------------------------------------------------------------- + +namespace { + +// Returns a Vector2d to allow us to do arithmetic on the result such as +// computing distances between centers. +gfx::Vector2d CenterOfRect(const gfx::Rect& rect) { + return rect.OffsetFromOrigin() + + gfx::Vector2d(rect.width() / 2, rect.height() / 2); +} + +} + +namespace gfx { + + +// RTreeBase::NodeBase -------------------------------------------------------- + +RTreeBase::NodeBase::~NodeBase() { +} + +void RTreeBase::NodeBase::RecomputeBoundsUpToRoot() { + RecomputeLocalBounds(); + if (parent_) + parent_->RecomputeBoundsUpToRoot(); +} + +RTreeBase::NodeBase::NodeBase(const Rect& rect, NodeBase* parent) + : rect_(rect), + parent_(parent) { +} + +void RTreeBase::NodeBase::RecomputeLocalBounds() { +} + +// RTreeBase::RecordBase ------------------------------------------------------ + +RTreeBase::RecordBase::RecordBase(const Rect& rect) : NodeBase(rect, NULL) { +} + +RTreeBase::RecordBase::~RecordBase() { +} + +void RTreeBase::RecordBase::AppendIntersectingRecords( + const Rect& query_rect, Records* matches_out) const { + if (rect().Intersects(query_rect)) + matches_out->push_back(this); +} + +void RTreeBase::RecordBase::AppendAllRecords(Records* matches_out) const { + matches_out->push_back(this); +} + +scoped_ptr<RTreeBase::NodeBase> +RTreeBase::RecordBase::RemoveAndReturnLastChild() { + return scoped_ptr<NodeBase>(); +} + +int RTreeBase::RecordBase::Level() const { + return -1; +} + + +// RTreeBase::Node ------------------------------------------------------------ + +RTreeBase::Node::Node() : NodeBase(Rect(), NULL), level_(0) { +} + +RTreeBase::Node::~Node() { +} + +scoped_ptr<RTreeBase::Node> RTreeBase::Node::ConstructParent() { + DCHECK(!parent()); + scoped_ptr<Node> new_parent(new Node(level_ + 1)); + new_parent->AddChild(scoped_ptr<NodeBase>(this)); + return new_parent.Pass(); +} + +void RTreeBase::Node::AppendIntersectingRecords( + const Rect& query_rect, Records* matches_out) const { + // Check own bounding box for intersection, can cull all children if no + // intersection. + if (!rect().Intersects(query_rect)) + return; + + // Conversely if we are completely contained within the query rect we can + // confidently skip all bounds checks for ourselves and all our children. + if (query_rect.Contains(rect())) { + AppendAllRecords(matches_out); + return; + } + + // We intersect the query rect but we are not are not contained within it. + // We must query each of our children in turn. + for (Nodes::const_iterator i = children_.begin(); i != children_.end(); ++i) + (*i)->AppendIntersectingRecords(query_rect, matches_out); +} + +void RTreeBase::Node::AppendAllRecords(Records* matches_out) const { + for (Nodes::const_iterator i = children_.begin(); i != children_.end(); ++i) + (*i)->AppendAllRecords(matches_out); +} + +void RTreeBase::Node::RemoveNodesForReinsert(size_t number_to_remove, + Nodes* nodes) { + DCHECK_LE(number_to_remove, children_.size()); + + std::partial_sort(children_.begin(), + children_.begin() + number_to_remove, + children_.end(), + &RTreeBase::Node::CompareCenterDistanceFromParent); + + // Move the lowest-distance nodes to the returned vector. + nodes->insert( + nodes->end(), children_.begin(), children_.begin() + number_to_remove); + children_.weak_erase(children_.begin(), children_.begin() + number_to_remove); +} + +scoped_ptr<RTreeBase::NodeBase> RTreeBase::Node::RemoveChild( + NodeBase* child_node, Nodes* orphans) { + DCHECK_EQ(this, child_node->parent()); + + scoped_ptr<NodeBase> orphan(child_node->RemoveAndReturnLastChild()); + while (orphan) { + orphans->push_back(orphan.release()); + orphan = child_node->RemoveAndReturnLastChild(); + } + + Nodes::iterator i = std::find(children_.begin(), children_.end(), child_node); + DCHECK(i != children_.end()); + children_.weak_erase(i); + + return scoped_ptr<NodeBase>(child_node); +} + +scoped_ptr<RTreeBase::NodeBase> RTreeBase::Node::RemoveAndReturnLastChild() { + if (children_.empty()) + return scoped_ptr<NodeBase>(); + + scoped_ptr<NodeBase> last_child(children_.back()); + children_.weak_erase(children_.end() - 1); + last_child->set_parent(NULL); + return last_child.Pass(); +} + +RTreeBase::Node* RTreeBase::Node::ChooseSubtree(NodeBase* node) { + DCHECK(node); + // Should never be called on a node at equal or lower level in the tree than + // the node to insert. + DCHECK_GT(level_, node->Level()); + + // If we are a parent of nodes on the provided node level, we are done. + if (level_ == node->Level() + 1) + return this; + + // Precompute a vector of expanded rects, used by both LeastOverlapIncrease + // and LeastAreaEnlargement. + Rects expanded_rects; + expanded_rects.reserve(children_.size()); + for (Nodes::iterator i = children_.begin(); i != children_.end(); ++i) + expanded_rects.push_back(UnionRects(node->rect(), (*i)->rect())); + + Node* best_candidate = NULL; + // For parents of leaf nodes, we pick the node that will cause the least + // increase in overlap by the addition of this new node. This may detect a + // tie, in which case it will return NULL. + if (level_ == 1) + best_candidate = LeastOverlapIncrease(node->rect(), expanded_rects); + + // For non-parents of leaf nodes, or for parents of leaf nodes with ties in + // overlap increase, we choose the subtree with least area enlargement caused + // by the addition of the new node. + if (!best_candidate) + best_candidate = LeastAreaEnlargement(node->rect(), expanded_rects); + + DCHECK(best_candidate); + return best_candidate->ChooseSubtree(node); +} + +size_t RTreeBase::Node::AddChild(scoped_ptr<NodeBase> node) { + DCHECK(node); + // Sanity-check that the level of the child being added is one less than ours. + DCHECK_EQ(level_ - 1, node->Level()); + node->set_parent(this); + set_rect(UnionRects(rect(), node->rect())); + children_.push_back(node.release()); + return children_.size(); +} + +scoped_ptr<RTreeBase::NodeBase> RTreeBase::Node::Split(size_t min_children, + size_t max_children) { + // We should have too many children to begin with. + DCHECK_EQ(max_children + 1, children_.size()); + + // Determine if we should split along the horizontal or vertical axis. + std::vector<NodeBase*> vertical_sort(children_.get()); + std::vector<NodeBase*> horizontal_sort(children_.get()); + std::sort(vertical_sort.begin(), + vertical_sort.end(), + &RTreeBase::Node::CompareVertical); + std::sort(horizontal_sort.begin(), + horizontal_sort.end(), + &RTreeBase::Node::CompareHorizontal); + + Rects low_vertical_bounds; + Rects low_horizontal_bounds; + BuildLowBounds(vertical_sort, + horizontal_sort, + &low_vertical_bounds, + &low_horizontal_bounds); + + Rects high_vertical_bounds; + Rects high_horizontal_bounds; + BuildHighBounds(vertical_sort, + horizontal_sort, + &high_vertical_bounds, + &high_horizontal_bounds); + + // Choose |end_index| such that both Nodes after the split will have + // min_children <= children_.size() <= max_children. + size_t end_index = std::min(max_children, children_.size() - min_children); + bool is_vertical_split = + SmallestMarginSum(min_children, + end_index, + low_horizontal_bounds, + high_horizontal_bounds) < + SmallestMarginSum(min_children, + end_index, + low_vertical_bounds, + high_vertical_bounds); + + // Choose split index along chosen axis and perform the split. + const Rects& low_bounds( + is_vertical_split ? low_vertical_bounds : low_horizontal_bounds); + const Rects& high_bounds( + is_vertical_split ? high_vertical_bounds : high_horizontal_bounds); + size_t split_index = + ChooseSplitIndex(min_children, end_index, low_bounds, high_bounds); + + const std::vector<NodeBase*>& sort( + is_vertical_split ? vertical_sort : horizontal_sort); + return DivideChildren(low_bounds, high_bounds, sort, split_index); +} + +int RTreeBase::Node::Level() const { + return level_; +} + +RTreeBase::Node::Node(int level) : NodeBase(Rect(), NULL), level_(level) { +} + +// static +bool RTreeBase::Node::CompareVertical(const NodeBase* a, const NodeBase* b) { + const Rect& a_rect = a->rect(); + const Rect& b_rect = b->rect(); + return (a_rect.y() < b_rect.y()) || + ((a_rect.y() == b_rect.y()) && (a_rect.height() < b_rect.height())); +} + +// static +bool RTreeBase::Node::CompareHorizontal(const NodeBase* a, const NodeBase* b) { + const Rect& a_rect = a->rect(); + const Rect& b_rect = b->rect(); + return (a_rect.x() < b_rect.x()) || + ((a_rect.x() == b_rect.x()) && (a_rect.width() < b_rect.width())); +} + +// static +bool RTreeBase::Node::CompareCenterDistanceFromParent(const NodeBase* a, + const NodeBase* b) { + const NodeBase* p = a->parent(); + + DCHECK(p); + DCHECK_EQ(p, b->parent()); + + Vector2d p_center = CenterOfRect(p->rect()); + Vector2d a_center = CenterOfRect(a->rect()); + Vector2d b_center = CenterOfRect(b->rect()); + + // We don't bother with square roots because we are only comparing the two + // values for sorting purposes. + return (a_center - p_center).LengthSquared() < + (b_center - p_center).LengthSquared(); +} + +// static +void RTreeBase::Node::BuildLowBounds( + const std::vector<NodeBase*>& vertical_sort, + const std::vector<NodeBase*>& horizontal_sort, + Rects* vertical_bounds, + Rects* horizontal_bounds) { + Rect vertical_bounds_rect; + vertical_bounds->reserve(vertical_sort.size()); + for (std::vector<NodeBase*>::const_iterator i = vertical_sort.begin(); + i != vertical_sort.end(); + ++i) { + vertical_bounds_rect.Union((*i)->rect()); + vertical_bounds->push_back(vertical_bounds_rect); + } + + Rect horizontal_bounds_rect; + horizontal_bounds->reserve(horizontal_sort.size()); + for (std::vector<NodeBase*>::const_iterator i = horizontal_sort.begin(); + i != horizontal_sort.end(); + ++i) { + horizontal_bounds_rect.Union((*i)->rect()); + horizontal_bounds->push_back(horizontal_bounds_rect); + } +} + +// static +void RTreeBase::Node::BuildHighBounds( + const std::vector<NodeBase*>& vertical_sort, + const std::vector<NodeBase*>& horizontal_sort, + Rects* vertical_bounds, + Rects* horizontal_bounds) { + Rect vertical_bounds_rect; + vertical_bounds->reserve(vertical_sort.size()); + for (std::vector<NodeBase*>::const_reverse_iterator i = + vertical_sort.rbegin(); + i != vertical_sort.rend(); + ++i) { + vertical_bounds_rect.Union((*i)->rect()); + vertical_bounds->push_back(vertical_bounds_rect); + } + std::reverse(vertical_bounds->begin(), vertical_bounds->end()); + + Rect horizontal_bounds_rect; + horizontal_bounds->reserve(horizontal_sort.size()); + for (std::vector<NodeBase*>::const_reverse_iterator i = + horizontal_sort.rbegin(); + i != horizontal_sort.rend(); + ++i) { + horizontal_bounds_rect.Union((*i)->rect()); + horizontal_bounds->push_back(horizontal_bounds_rect); + } + std::reverse(horizontal_bounds->begin(), horizontal_bounds->end()); +} + +size_t RTreeBase::Node::ChooseSplitIndex(size_t start_index, + size_t end_index, + const Rects& low_bounds, + const Rects& high_bounds) { + DCHECK_EQ(low_bounds.size(), high_bounds.size()); + + int smallest_overlap_area = UnionRects( + low_bounds[start_index], high_bounds[start_index]).size().GetArea(); + int smallest_combined_area = low_bounds[start_index].size().GetArea() + + high_bounds[start_index].size().GetArea(); + size_t optimal_split_index = start_index; + for (size_t p = start_index + 1; p < end_index; ++p) { + const int overlap_area = + UnionRects(low_bounds[p], high_bounds[p]).size().GetArea(); + const int combined_area = + low_bounds[p].size().GetArea() + high_bounds[p].size().GetArea(); + if ((overlap_area < smallest_overlap_area) || + ((overlap_area == smallest_overlap_area) && + (combined_area < smallest_combined_area))) { + smallest_overlap_area = overlap_area; + smallest_combined_area = combined_area; + optimal_split_index = p; + } + } + + // optimal_split_index currently points at the last element in the first set, + // so advance it by 1 to point at the first element in the second set. + return optimal_split_index + 1; +} + +// static +int RTreeBase::Node::SmallestMarginSum(size_t start_index, + size_t end_index, + const Rects& low_bounds, + const Rects& high_bounds) { + DCHECK_EQ(low_bounds.size(), high_bounds.size()); + DCHECK_LT(start_index, low_bounds.size()); + DCHECK_LE(start_index, end_index); + DCHECK_LE(end_index, low_bounds.size()); + Rects::const_iterator i(low_bounds.begin() + start_index); + Rects::const_iterator j(high_bounds.begin() + start_index); + int smallest_sum = i->width() + i->height() + j->width() + j->height(); + for (; i != (low_bounds.begin() + end_index); ++i, ++j) { + smallest_sum = std::min( + smallest_sum, i->width() + i->height() + j->width() + j->height()); + } + + return smallest_sum; +} + +void RTreeBase::Node::RecomputeLocalBounds() { + Rect bounds; + for (size_t i = 0; i < children_.size(); ++i) + bounds.Union(children_[i]->rect()); + + set_rect(bounds); +} + +int RTreeBase::Node::OverlapIncreaseToAdd(const Rect& rect, + const NodeBase* candidate_node, + const Rect& expanded_rect) const { + DCHECK(candidate_node); + + // Early-out when |rect| is contained completely within |candidate|. + if (candidate_node->rect().Contains(rect)) + return 0; + + int total_original_overlap = 0; + int total_expanded_overlap = 0; + + // Now calculate overlap with all other rects in this node. + for (Nodes::const_iterator it = children_.begin(); + it != children_.end(); ++it) { + // Skip calculating overlap with the candidate rect. + if ((*it) == candidate_node) + continue; + NodeBase* overlap_node = (*it); + total_original_overlap += IntersectRects( + candidate_node->rect(), overlap_node->rect()).size().GetArea(); + Rect expanded_overlap_rect = expanded_rect; + expanded_overlap_rect.Intersect(overlap_node->rect()); + total_expanded_overlap += expanded_overlap_rect.size().GetArea(); + } + + return total_expanded_overlap - total_original_overlap; +} + +scoped_ptr<RTreeBase::NodeBase> RTreeBase::Node::DivideChildren( + const Rects& low_bounds, + const Rects& high_bounds, + const std::vector<NodeBase*>& sorted_children, + size_t split_index) { + DCHECK_EQ(low_bounds.size(), high_bounds.size()); + DCHECK_EQ(low_bounds.size(), sorted_children.size()); + DCHECK_LT(split_index, low_bounds.size()); + DCHECK_GT(split_index, 0U); + + scoped_ptr<Node> sibling(new Node(level_)); + sibling->set_parent(parent()); + set_rect(low_bounds[split_index - 1]); + sibling->set_rect(high_bounds[split_index]); + + // Our own children_ vector is unsorted, so we wipe it out and divide the + // sorted bounds rects between ourselves and our sibling. + children_.weak_clear(); + children_.insert(children_.end(), + sorted_children.begin(), + sorted_children.begin() + split_index); + sibling->children_.insert(sibling->children_.end(), + sorted_children.begin() + split_index, + sorted_children.end()); + + for (size_t i = 0; i < sibling->children_.size(); ++i) + sibling->children_[i]->set_parent(sibling.get()); + + return sibling.PassAs<NodeBase>(); +} + +RTreeBase::Node* RTreeBase::Node::LeastOverlapIncrease( + const Rect& node_rect, + const Rects& expanded_rects) { + NodeBase* best_node = children_.front(); + int least_overlap_increase = + OverlapIncreaseToAdd(node_rect, children_[0], expanded_rects[0]); + for (size_t i = 1; i < children_.size(); ++i) { + int overlap_increase = + OverlapIncreaseToAdd(node_rect, children_[i], expanded_rects[i]); + if (overlap_increase < least_overlap_increase) { + least_overlap_increase = overlap_increase; + best_node = children_[i]; + } else if (overlap_increase == least_overlap_increase) { + // If we are tied at zero there is no possible better overlap increase, + // so we can report a tie early. + if (overlap_increase == 0) + return NULL; + + best_node = NULL; + } + } + + // Ensure that our children are always Nodes and not Records. + DCHECK_GE(level_, 1); + return static_cast<Node*>(best_node); +} + +RTreeBase::Node* RTreeBase::Node::LeastAreaEnlargement( + const Rect& node_rect, + const Rects& expanded_rects) { + DCHECK(!children_.empty()); + DCHECK_EQ(children_.size(), expanded_rects.size()); + + NodeBase* best_node = children_.front(); + int least_area_enlargement = + expanded_rects[0].size().GetArea() - best_node->rect().size().GetArea(); + for (size_t i = 1; i < children_.size(); ++i) { + NodeBase* candidate_node = children_[i]; + int area_change = expanded_rects[i].size().GetArea() - + candidate_node->rect().size().GetArea(); + DCHECK_GE(area_change, 0); + if (area_change < least_area_enlargement) { + best_node = candidate_node; + least_area_enlargement = area_change; + } else if (area_change == least_area_enlargement && + candidate_node->rect().size().GetArea() < + best_node->rect().size().GetArea()) { + // Ties are broken by choosing the entry with the least area. + best_node = candidate_node; + } + } + + // Ensure that our children are always Nodes and not Records. + DCHECK_GE(level_, 1); + return static_cast<Node*>(best_node); +} + + +// RTreeBase ------------------------------------------------------------------ + +RTreeBase::RTreeBase(size_t min_children, size_t max_children) + : root_(new Node()), + min_children_(min_children), + max_children_(max_children) { + DCHECK_GE(min_children_, 2U); + DCHECK_LE(min_children_, max_children_ / 2U); +} + +RTreeBase::~RTreeBase() { +} + +void RTreeBase::InsertNode( + scoped_ptr<NodeBase> node, int* highest_reinsert_level) { + // Find the most appropriate parent to insert node into. + Node* parent = root_->ChooseSubtree(node.get()); + DCHECK(parent); + // Verify ChooseSubtree returned a Node at the correct level. + DCHECK_EQ(parent->Level(), node->Level() + 1); + Node* insert_parent = static_cast<Node*>(parent); + NodeBase* needs_bounds_recomputed = insert_parent->parent(); + Nodes reinserts; + // Attempt to insert the Node, if this overflows the Node we must handle it. + while (insert_parent && + insert_parent->AddChild(node.Pass()) > max_children_) { + // If we have yet to re-insert nodes at this level during this data insert, + // and we're not at the root, R*-Tree calls for re-insertion of some of the + // nodes, resulting in a better balance on the tree. + if (insert_parent->parent() && + insert_parent->Level() > *highest_reinsert_level) { + insert_parent->RemoveNodesForReinsert(max_children_ / 3, &reinserts); + // Adjust highest_reinsert_level to this level. + *highest_reinsert_level = insert_parent->Level(); + // RemoveNodesForReinsert() does not recompute bounds, so mark it. + needs_bounds_recomputed = insert_parent; + break; + } + + // Split() will create a sibling to insert_parent both of which will have + // valid bounds, but this invalidates their parent's bounds. + node = insert_parent->Split(min_children_, max_children_); + insert_parent = static_cast<Node*>(insert_parent->parent()); + needs_bounds_recomputed = insert_parent; + } + + // If we have a Node to insert, and we hit the root of the current tree, + // we create a new root which is the parent of the current root and the + // insert_node. Note that we must release() the |root_| since + // ConstructParent() will take ownership of it. + if (!insert_parent && node) { + root_ = root_.release()->ConstructParent(); + root_->AddChild(node.Pass()); + } + + // Recompute bounds along insertion path. + if (needs_bounds_recomputed) + needs_bounds_recomputed->RecomputeBoundsUpToRoot(); + + // Complete re-inserts, if any. The algorithm only allows for one invocation + // of RemoveNodesForReinsert() per level of the tree in an overall call to + // Insert(). + while (!reinserts.empty()) { + Nodes::iterator last_element = reinserts.end() - 1; + NodeBase* temp_ptr(*last_element); + reinserts.weak_erase(last_element); + InsertNode(make_scoped_ptr(temp_ptr), highest_reinsert_level); + } +} + +scoped_ptr<RTreeBase::NodeBase> RTreeBase::RemoveNode(NodeBase* node) { + // We need to remove this node from its parent. + Node* parent = static_cast<Node*>(node->parent()); + // Record nodes are never allowed as the root, so we should always have a + // parent. + DCHECK(parent); + // Should always be a leaf that had the record. + DCHECK_EQ(0, parent->Level()); + + Nodes orphans; + scoped_ptr<NodeBase> removed_node(parent->RemoveChild(node, &orphans)); + + // It's possible that by removing |node| from |parent| we have made |parent| + // have less than the minimum number of children, in which case we will need + // to remove and delete |parent| while reinserting any other children that it + // had. We traverse up the tree doing this until we remove a child from a + // parent that still has greater than or equal to the minimum number of Nodes. + while (parent->count() < min_children_) { + NodeBase* child = parent; + parent = static_cast<Node*>(parent->parent()); + + // If we've hit the root, stop. + if (!parent) + break; + + parent->RemoveChild(child, &orphans); + } + + // If we stopped deleting nodes up the tree before encountering the root, + // we'll need to fix up the bounds from the first parent we didn't delete + // up to the root. + if (parent) + parent->RecomputeBoundsUpToRoot(); + else + root_->RecomputeBoundsUpToRoot(); + + while (!orphans.empty()) { + Nodes::iterator last_element = orphans.end() - 1; + NodeBase* temp_ptr(*last_element); + orphans.weak_erase(last_element); + int starting_level = -1; + InsertNode(make_scoped_ptr(temp_ptr), &starting_level); + } + + return removed_node.Pass(); +} + +void RTreeBase::PruneRootIfNecessary() { + if (root()->count() == 1 && root()->Level() > 0) { + // Awkward reset(cast(release)) pattern here because there's no better way + // to downcast the scoped_ptr from RemoveAndReturnLastChild() from NodeBase + // to Node. + root_.reset( + static_cast<Node*>(root_->RemoveAndReturnLastChild().release())); + } +} + +void RTreeBase::ResetRoot() { + root_.reset(new Node()); +} + +} // namespace gfx diff --git a/chromium/ui/gfx/geometry/r_tree_base.h b/chromium/ui/gfx/geometry/r_tree_base.h new file mode 100644 index 00000000000..21dc86b2d4d --- /dev/null +++ b/chromium/ui/gfx/geometry/r_tree_base.h @@ -0,0 +1,309 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Provides an implementation the parts of the RTree data structure that don't +// require knowledge of the generic key type. Don't use these objects directly, +// rather specialize the RTree<> object in r_tree.h. This file defines the +// internal objects of an RTree, namely Nodes (internal nodes of the tree) and +// Records, which hold (key, rectangle) pairs. + +#ifndef UI_GFX_GEOMETRY_R_TREE_BASE_H_ +#define UI_GFX_GEOMETRY_R_TREE_BASE_H_ + +#include <list> +#include <vector> + +#include "base/containers/hash_tables.h" +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/scoped_vector.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/gfx_export.h" + +namespace gfx { + +class GFX_EXPORT RTreeBase { + protected: + class NodeBase; + class RecordBase; + + typedef std::vector<const RecordBase*> Records; + typedef ScopedVector<NodeBase> Nodes; + + RTreeBase(size_t min_children, size_t max_children); + ~RTreeBase(); + + // Protected data structure class for storing internal Nodes or leaves with + // Records. + class GFX_EXPORT NodeBase { + public: + virtual ~NodeBase(); + + // Appends to |records_out| the set of Records in this subtree with rects + // that intersect |query_rect|. Avoids clearing |records_out| so that it + // can be called recursively. + virtual void AppendIntersectingRecords(const Rect& query_rect, + Records* records_out) const = 0; + + // Returns all records stored in the subtree rooted at this node. Appends to + // |matches_out| without clearing. + virtual void AppendAllRecords(Records* records_out) const = 0; + + // Returns NULL if no children. Does not recompute bounds. + virtual scoped_ptr<NodeBase> RemoveAndReturnLastChild() = 0; + + // Returns -1 for Records, or the height of this subtree for Nodes. The + // height of a leaf Node (a Node containing only Records) is 0, a leaf's + // parent is 1, etc. Note that in an R*-Tree, all branches from the root + // Node will be the same height. + virtual int Level() const = 0; + + // Recomputes our bounds by taking the union of all child rects, then calls + // recursively on our parent so that ultimately all nodes up to the root + // recompute their bounds. + void RecomputeBoundsUpToRoot(); + + NodeBase* parent() { return parent_; } + const NodeBase* parent() const { return parent_; } + void set_parent(NodeBase* parent) { parent_ = parent; } + const Rect& rect() const { return rect_; } + void set_rect(const Rect& rect) { rect_ = rect; } + + protected: + NodeBase(const Rect& rect, NodeBase* parent); + + // Bounds recomputation without calling parents to do the same. + virtual void RecomputeLocalBounds(); + + private: + friend class RTreeTest; + friend class RTreeNodeTest; + + // This Node's bounding rectangle. + Rect rect_; + + // A weak pointer to our parent Node in the RTree. The root node will have a + // NULL value for |parent_|. + NodeBase* parent_; + + DISALLOW_COPY_AND_ASSIGN(NodeBase); + }; + + class GFX_EXPORT RecordBase : public NodeBase { + public: + explicit RecordBase(const Rect& rect); + virtual ~RecordBase(); + + virtual void AppendIntersectingRecords(const Rect& query_rect, + Records* records_out) const OVERRIDE; + virtual void AppendAllRecords(Records* records_out) const OVERRIDE; + virtual scoped_ptr<NodeBase> RemoveAndReturnLastChild() OVERRIDE; + virtual int Level() const OVERRIDE; + + private: + friend class RTreeTest; + friend class RTreeNodeTest; + + DISALLOW_COPY_AND_ASSIGN(RecordBase); + }; + + class GFX_EXPORT Node : public NodeBase { + public: + // Constructs an empty Node with |level_| of 0. + Node(); + virtual ~Node(); + + virtual void AppendIntersectingRecords(const Rect& query_rect, + Records* records_out) const OVERRIDE; + virtual scoped_ptr<NodeBase> RemoveAndReturnLastChild() OVERRIDE; + virtual int Level() const OVERRIDE; + virtual void AppendAllRecords(Records* matches_out) const OVERRIDE; + + // Constructs a new Node that is the parent of this Node and already has + // this Node as its sole child. Valid to call only on root Nodes, meaning + // Nodes with |parent_| NULL. Note that ownership of this Node is + // transferred to the parent returned by this function. + scoped_ptr<Node> ConstructParent(); + + // Removes |number_to_remove| children from this Node, and appends them to + // the supplied list. Does not repair bounds upon completion. Nodes are + // selected in the manner suggested in the Beckmann et al. paper, which + // suggests that the children should be sorted by the distance from the + // center of their bounding rectangle to their parent's bounding rectangle, + // and then the n closest children should be removed for re-insertion. This + // removal occurs at most once on each level of the tree when overflowing + // nodes that have exceeded the maximum number of children during an Insert. + void RemoveNodesForReinsert(size_t number_to_remove, Nodes* nodes); + + // Given a pointer to a child node within this Node, removes it from our + // list. If that child had any children, appends them to the supplied orphan + // list. Returns the removed child. Does not recompute bounds, as the caller + // might subsequently remove this node as well, meaning the recomputation + // would be wasted work. + scoped_ptr<NodeBase> RemoveChild(NodeBase* child_node, Nodes* orphans); + + // Returns the best parent for insertion of the provided |node| as a child. + Node* ChooseSubtree(NodeBase* node); + + // Adds |node| as a child of this Node, and recomputes the bounds of this + // node after the addition of the child. Returns the new count of children + // stored in this Node. This node becomes the owner of |node|. + size_t AddChild(scoped_ptr<NodeBase> node); + + // Returns a sibling to this Node with at least min_children and no greater + // than max_children of this Node's children assigned to it, and having the + // same parent. Bounds will be valid on both Nodes after this call. + scoped_ptr<NodeBase> Split(size_t min_children, size_t max_children); + + size_t count() const { return children_.size(); } + const NodeBase* child(size_t i) const { return children_[i]; } + NodeBase* child(size_t i) { return children_[i]; } + + private: + typedef std::vector<Rect> Rects; + + explicit Node(int level); + + // Given two arrays of bounds rectangles as computed by BuildLowBounds() + // and BuildHighBounds(), returns the index of the element in those arrays + // along which a split of the arrays would result in a minimum amount of + // overlap (area of intersection) in the two groups. + static size_t ChooseSplitIndex(size_t start_index, + size_t end_index, + const Rects& low_bounds, + const Rects& high_bounds); + + // R*-Tree attempts to keep groups of rectangles that are roughly square + // in shape. It does this by comparing the "margins" of different bounding + // boxes, where margin is defined as the sum of the length of all four sides + // of a rectangle. For two rectangles of equal area, the one with the + // smallest margin will be the rectangle whose width and height differ the + // least. When splitting we decide to split along an axis chosen from the + // rectangles either sorted vertically or horizontally by finding the axis + // that would result in the smallest sum of margins between the two bounding + // boxes of the resulting split. Returns the smallest sum computed given the + // sorted bounding boxes and a range to look within. + static int SmallestMarginSum(size_t start_index, + size_t end_index, + const Rects& low_bounds, + const Rects& high_bounds); + + // Sorts nodes primarily by increasing y coordinates, and secondarily by + // increasing height. + static bool CompareVertical(const NodeBase* a, const NodeBase* b); + + // Sorts nodes primarily by increasing x coordinates, and secondarily by + // increasing width. + static bool CompareHorizontal(const NodeBase* a, const NodeBase* b); + + // Sorts nodes by the distance of the center of their rectangles to the + // center of their parent's rectangles. + static bool CompareCenterDistanceFromParent( + const NodeBase* a, const NodeBase* b); + + // Given two vectors of Nodes sorted by vertical or horizontal bounds, + // populates two vectors of Rectangles in which the ith element is the union + // of all bounding rectangles [0,i] in the associated sorted array of Nodes. + static void BuildLowBounds(const std::vector<NodeBase*>& vertical_sort, + const std::vector<NodeBase*>& horizontal_sort, + Rects* vertical_bounds, + Rects* horizontal_bounds); + + // Given two vectors of Nodes sorted by vertical or horizontal bounds, + // populates two vectors of Rectangles in which the ith element is the + // union of all bounding rectangles [i, count()) in the associated sorted + // array of Nodes. + static void BuildHighBounds(const std::vector<NodeBase*>& vertical_sort, + const std::vector<NodeBase*>& horizontal_sort, + Rects* vertical_bounds, + Rects* horizontal_bounds); + + virtual void RecomputeLocalBounds() OVERRIDE; + + // Returns the increase in overlap value, as defined in Beckmann et al. as + // the sum of the areas of the intersection of all child rectangles + // (excepting the candidate child) with the argument rectangle. Here the + // |candidate_node| is one of our |children_|, and |expanded_rect| is the + // already-computed union of the candidate's rect and |rect|. + int OverlapIncreaseToAdd(const Rect& rect, + const NodeBase* candidate_node, + const Rect& expanded_rect) const; + + // Returns a new node containing children [split_index, count()) within + // |sorted_children|. Children before |split_index| remain with |this|. + scoped_ptr<NodeBase> DivideChildren( + const Rects& low_bounds, + const Rects& high_bounds, + const std::vector<NodeBase*>& sorted_children, + size_t split_index); + + // Returns a pointer to the child node that will result in the least overlap + // increase with the addition of node_rect, or NULL if there's a tie found. + // Requires a precomputed vector of expanded rectangles where the ith + // rectangle in the vector is the union of |children_|[i] and node_rect. + // Overlap is defined in Beckmann et al. as the sum of the areas of + // intersection of all child rectangles with the |node_rect| argument + // rectangle. This heuristic attempts to choose the node for which adding + // the new rectangle to their bounding box will result in the least overlap + // with the other rectangles, thus trying to preserve the usefulness of the + // bounding rectangle by keeping it from covering too much redundant area. + Node* LeastOverlapIncrease(const Rect& node_rect, + const Rects& expanded_rects); + + // Returns a pointer to the child node that will result in the least area + // enlargement if the argument node rectangle were to be added to that + // node's bounding box. Requires a precomputed vector of expanded rectangles + // where the ith rectangle in the vector is the union of children_[i] and + // |node_rect|. + Node* LeastAreaEnlargement(const Rect& node_rect, + const Rects& expanded_rects); + + const int level_; + + Nodes children_; + + friend class RTreeTest; + friend class RTreeNodeTest; + + DISALLOW_COPY_AND_ASSIGN(Node); + }; + + // Inserts |node| into the tree. The |highest_reinsert_level| supports + // re-insertion as described by Beckmann et al. As Node overflows progagate + // up the tree the algorithm performs a reinsertion of the overflow Nodes + // (instead of a split) at most once per level of the tree. A starting value + // of -1 for |highest_reinsert_level| means that reinserts are permitted for + // every level of the tree. This should always be set to -1 except by + // recursive calls from within InsertNode(). + void InsertNode(scoped_ptr<NodeBase> node, int* highest_reinsert_level); + + // Removes |node| from the tree without deleting it. + scoped_ptr<NodeBase> RemoveNode(NodeBase* node); + + // If |root_| has only one child, deletes the |root_| Node and replaces it + // with its only descendant child. Otherwise does nothing. + void PruneRootIfNecessary(); + + // Deletes the entire current tree and replaces it with an empty Node. + void ResetRoot(); + + const Node* root() const { return root_.get(); } + + private: + friend class RTreeTest; + friend class RTreeNodeTest; + + // A pointer to the root node in the RTree. + scoped_ptr<Node> root_; + + // The parameters used to define the shape of the RTree. + const size_t min_children_; + const size_t max_children_; + + DISALLOW_COPY_AND_ASSIGN(RTreeBase); +}; + +} // namespace gfx + +#endif // UI_GFX_GEOMETRY_R_TREE_BASE_H_ diff --git a/chromium/ui/gfx/geometry/r_tree_unittest.cc b/chromium/ui/gfx/geometry/r_tree_unittest.cc new file mode 100644 index 00000000000..ba45405ee2a --- /dev/null +++ b/chromium/ui/gfx/geometry/r_tree_unittest.cc @@ -0,0 +1,1025 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/gfx/geometry/r_tree.h" +#include "ui/gfx/geometry/r_tree_base.h" +#include "ui/gfx/geometry/rect.h" + +namespace gfx { + +class RTreeTest : public ::testing::Test { + protected: + typedef RTree<int> RT; + + // Given a pointer to an RTree, traverse it and verify that its internal + // structure is consistent with RTree semantics. + void ValidateRTree(RTreeBase* rt) { + // If RTree is empty it should have an empty rectangle. + if (!rt->root()->count()) { + EXPECT_TRUE(rt->root()->rect().IsEmpty()); + EXPECT_EQ(0, rt->root()->Level()); + return; + } + // Root is allowed to have fewer than min_children_ but never more than + // max_children_. + EXPECT_LE(rt->root()->count(), rt->max_children_); + // The root should never be a record node. + EXPECT_GT(rt->root()->Level(), -1); + // The root should never have a parent pointer. + EXPECT_TRUE(rt->root()->parent() == NULL); + // Bounds must be consistent on the root. + CheckBoundsConsistent(rt->root()); + for (size_t i = 0; i < rt->root()->count(); ++i) { + ValidateNode( + rt->root()->child(i), rt->min_children_, rt->max_children_); + } + } + + // Recursive descent method used by ValidateRTree to check each node within + // the RTree for consistency with RTree semantics. + void ValidateNode(const RTreeBase::NodeBase* node_base, + size_t min_children, + size_t max_children) { + if (node_base->Level() >= 0) { + const RTreeBase::Node* node = + static_cast<const RTreeBase::Node*>(node_base); + EXPECT_GE(node->count(), min_children); + EXPECT_LE(node->count(), max_children); + CheckBoundsConsistent(node); + for (size_t i = 0; i < node->count(); ++i) + ValidateNode(node->child(i), min_children, max_children); + } + } + + // Check bounds are consistent with children bounds, and other checks + // convenient to do while enumerating the children of node. + void CheckBoundsConsistent(const RTreeBase::Node* node) { + EXPECT_FALSE(node->rect().IsEmpty()); + Rect check_bounds; + for (size_t i = 0; i < node->count(); ++i) { + const RTreeBase::NodeBase* child_node = node->child(i); + check_bounds.Union(child_node->rect()); + EXPECT_EQ(node->Level() - 1, child_node->Level()); + EXPECT_EQ(node, child_node->parent()); + } + EXPECT_EQ(check_bounds, node->rect()); + } + + // Adds count squares stacked around the point (0,0) with key equal to width. + void AddStackedSquares(RT* rt, int count) { + for (int i = 1; i <= count; ++i) { + rt->Insert(Rect(0, 0, i, i), i); + ValidateRTree(static_cast<RTreeBase*>(rt)); + } + } + + // Given an unordered list of matching keys, verifies that it contains all + // values [1..length] for the length of that list. + void VerifyAllKeys(const RT::Matches& keys) { + for (size_t i = 1; i <= keys.size(); ++i) + EXPECT_EQ(1U, keys.count(i)); + } + + // Given a node and a rectangle, builds an expanded rectangle list where the + // ith element of the vector is the union of the rectangle of the ith child of + // the node and the argument rectangle. + void BuildExpandedRects(RTreeBase::Node* node, + const Rect& rect, + std::vector<Rect>* expanded_rects) { + expanded_rects->clear(); + expanded_rects->reserve(node->count()); + for (size_t i = 0; i < node->count(); ++i) { + Rect expanded_rect(rect); + expanded_rect.Union(node->child(i)->rect()); + expanded_rects->push_back(expanded_rect); + } + } +}; + +class RTreeNodeTest : public RTreeTest { + protected: + typedef RTreeBase::NodeBase RTreeNodeBase; + typedef RT::Record RTreeRecord; + typedef RTreeBase::Node RTreeNode; + typedef RTreeBase::Node::Rects RTreeRects; + typedef RTreeBase::Nodes RTreeNodes; + + // Accessors to private members of RTree::Node. + const RTreeRecord* record(RTreeNode* node, size_t i) { + return static_cast<const RTreeRecord*>(node->child(i)); + } + + // Provides access for tests to private methods of RTree::Node. + scoped_ptr<RTreeNode> NewNodeAtLevel(size_t level) { + return make_scoped_ptr(new RTreeBase::Node(level)); + } + + void NodeRecomputeLocalBounds(RTreeNodeBase* node) { + node->RecomputeLocalBounds(); + } + + bool NodeCompareVertical(RTreeNodeBase* a, RTreeNodeBase* b) { + return RTreeBase::Node::CompareVertical(a, b); + } + + bool NodeCompareHorizontal(RTreeNodeBase* a, RTreeNodeBase* b) { + return RTreeBase::Node::CompareHorizontal(a, b); + } + + bool NodeCompareCenterDistanceFromParent( + const RTreeNodeBase* a, const RTreeNodeBase* b) { + return RTreeBase::Node::CompareCenterDistanceFromParent(a, b); + } + + int NodeOverlapIncreaseToAdd(RTreeNode* node, + const Rect& rect, + const RTreeNodeBase* candidate_node, + const Rect& expanded_rect) { + return node->OverlapIncreaseToAdd(rect, candidate_node, expanded_rect); + } + + void NodeBuildLowBounds(const std::vector<RTreeNodeBase*>& vertical_sort, + const std::vector<RTreeNodeBase*>& horizontal_sort, + RTreeRects* vertical_bounds, + RTreeRects* horizontal_bounds) { + RTreeBase::Node::BuildLowBounds( + vertical_sort, horizontal_sort, vertical_bounds, horizontal_bounds); + } + + void NodeBuildHighBounds(const std::vector<RTreeNodeBase*>& vertical_sort, + const std::vector<RTreeNodeBase*>& horizontal_sort, + RTreeRects* vertical_bounds, + RTreeRects* horizontal_bounds) { + RTreeBase::Node::BuildHighBounds( + vertical_sort, horizontal_sort, vertical_bounds, horizontal_bounds); + } + + int NodeSmallestMarginSum(size_t start_index, + size_t end_index, + const RTreeRects& low_bounds, + const RTreeRects& high_bounds) { + return RTreeBase::Node::SmallestMarginSum( + start_index, end_index, low_bounds, high_bounds); + } + + size_t NodeChooseSplitIndex(size_t min_children, + size_t max_children, + const RTreeRects& low_bounds, + const RTreeRects& high_bounds) { + return RTreeBase::Node::ChooseSplitIndex( + min_children, max_children, low_bounds, high_bounds); + } + + scoped_ptr<RTreeNodeBase> NodeDivideChildren( + RTreeNode* node, + const RTreeRects& low_bounds, + const RTreeRects& high_bounds, + const std::vector<RTreeNodeBase*>& sorted_children, + size_t split_index) { + return node->DivideChildren( + low_bounds, high_bounds, sorted_children, split_index); + } + + RTreeNode* NodeLeastOverlapIncrease(RTreeNode* node, + const Rect& node_rect, + const RTreeRects& expanded_rects) { + return node->LeastOverlapIncrease(node_rect, expanded_rects); + } + + RTreeNode* NodeLeastAreaEnlargement(RTreeNode* node, + const Rect& node_rect, + const RTreeRects& expanded_rects) { + return node->LeastAreaEnlargement(node_rect, expanded_rects); + } +}; + +// RTreeNodeTest -------------------------------------------------------------- + +TEST_F(RTreeNodeTest, RemoveNodesForReinsert) { + // Make a leaf node for testing removal from. + scoped_ptr<RTreeNode> test_node(new RTreeNode); + // Build 20 record nodes with rectangle centers going from (1,1) to (20,20) + for (int i = 1; i <= 20; ++i) + test_node->AddChild(scoped_ptr<RTreeNodeBase>( + new RTreeRecord(Rect(i - 1, i - 1, 2, 2), i))); + + // Quick verification of the node before removing children. + ValidateNode(test_node.get(), 1U, 20U); + // Use a scoped vector to delete all children that get removed from the Node. + RTreeNodes removals; + test_node->RemoveNodesForReinsert(1, &removals); + // Should have gotten back 1 node pointer. + EXPECT_EQ(1U, removals.size()); + // There should be 19 left in the test_node. + EXPECT_EQ(19U, test_node->count()); + // If we fix up the bounds on the test_node, it should verify. + NodeRecomputeLocalBounds(test_node.get()); + ValidateNode(test_node.get(), 2U, 20U); + // The node we removed should be node 10, as it was exactly in the center. + EXPECT_EQ(10, static_cast<RTreeRecord*>(removals[0])->key()); + + // Now remove the next 2. + removals.clear(); + test_node->RemoveNodesForReinsert(2, &removals); + EXPECT_EQ(2U, removals.size()); + EXPECT_EQ(17U, test_node->count()); + NodeRecomputeLocalBounds(test_node.get()); + ValidateNode(test_node.get(), 2U, 20U); + // Lastly the 2 nodes we should have gotten back are keys 9 and 11, as their + // centers were the closest to the center of the node bounding box. + base::hash_set<intptr_t> results_hash; + results_hash.insert(static_cast<RTreeRecord*>(removals[0])->key()); + results_hash.insert(static_cast<RTreeRecord*>(removals[1])->key()); + EXPECT_EQ(1U, results_hash.count(9)); + EXPECT_EQ(1U, results_hash.count(11)); +} + +TEST_F(RTreeNodeTest, CompareVertical) { + // One rect with lower y than another should always sort lower. + RTreeRecord low(Rect(0, 1, 10, 10), 1); + RTreeRecord middle(Rect(0, 5, 10, 10), 5); + EXPECT_TRUE(NodeCompareVertical(&low, &middle)); + EXPECT_FALSE(NodeCompareVertical(&middle, &low)); + + // Try a non-overlapping higher-y rectangle. + RTreeRecord high(Rect(-10, 20, 10, 1), 10); + EXPECT_TRUE(NodeCompareVertical(&low, &high)); + EXPECT_FALSE(NodeCompareVertical(&high, &low)); + + // Ties are broken by lowest bottom y value. + RTreeRecord shorter_tie(Rect(10, 1, 100, 2), 2); + EXPECT_TRUE(NodeCompareVertical(&shorter_tie, &low)); + EXPECT_FALSE(NodeCompareVertical(&low, &shorter_tie)); +} + +TEST_F(RTreeNodeTest, CompareHorizontal) { + // One rect with lower x than another should always sort lower than higher x. + RTreeRecord low(Rect(1, 0, 10, 10), 1); + RTreeRecord middle(Rect(5, 0, 10, 10), 5); + EXPECT_TRUE(NodeCompareHorizontal(&low, &middle)); + EXPECT_FALSE(NodeCompareHorizontal(&middle, &low)); + + // Try a non-overlapping higher-x rectangle. + RTreeRecord high(Rect(20, -10, 1, 10), 10); + EXPECT_TRUE(NodeCompareHorizontal(&low, &high)); + EXPECT_FALSE(NodeCompareHorizontal(&high, &low)); + + // Ties are broken by lowest bottom x value. + RTreeRecord shorter_tie(Rect(1, 10, 2, 100), 2); + EXPECT_TRUE(NodeCompareHorizontal(&shorter_tie, &low)); + EXPECT_FALSE(NodeCompareHorizontal(&low, &shorter_tie)); +} + +TEST_F(RTreeNodeTest, CompareCenterDistanceFromParent) { + // Create a test node we can add children to, for distance comparisons. + scoped_ptr<RTreeNode> parent(new RTreeNode); + + // Add three children, one each with centers at (0, 0), (10, 10), (-9, -9), + // around which a bounding box will be centered at (0, 0) + scoped_ptr<RTreeRecord> center_zero(new RTreeRecord(Rect(-1, -1, 2, 2), 1)); + parent->AddChild(center_zero.PassAs<RTreeNodeBase>()); + + scoped_ptr<RTreeRecord> center_positive(new RTreeRecord(Rect(9, 9, 2, 2), 2)); + parent->AddChild(center_positive.PassAs<RTreeNodeBase>()); + + scoped_ptr<RTreeRecord> center_negative( + new RTreeRecord(Rect(-10, -10, 2, 2), 3)); + parent->AddChild(center_negative.PassAs<RTreeNodeBase>()); + + ValidateNode(parent.get(), 1U, 5U); + EXPECT_EQ(Rect(-10, -10, 21, 21), parent->rect()); + + EXPECT_TRUE( + NodeCompareCenterDistanceFromParent(parent->child(0), parent->child(1))); + EXPECT_FALSE( + NodeCompareCenterDistanceFromParent(parent->child(1), parent->child(0))); + EXPECT_TRUE( + NodeCompareCenterDistanceFromParent(parent->child(0), parent->child(2))); + EXPECT_FALSE( + NodeCompareCenterDistanceFromParent(parent->child(2), parent->child(0))); + EXPECT_TRUE( + NodeCompareCenterDistanceFromParent(parent->child(2), parent->child(1))); + EXPECT_FALSE( + NodeCompareCenterDistanceFromParent(parent->child(1), parent->child(2))); +} + +TEST_F(RTreeNodeTest, OverlapIncreaseToAdd) { + // Create a test node with three children, for overlap comparisons. + scoped_ptr<RTreeNode> parent(new RTreeNode); + + // Add three children, each 4 wide and tall, at (0, 0), (3, 3), (6, 6) with + // overlapping corners. + Rect top(0, 0, 4, 4); + parent->AddChild(scoped_ptr<RTreeNodeBase>(new RTreeRecord(top, 1))); + Rect middle(3, 3, 4, 4); + parent->AddChild(scoped_ptr<RTreeNodeBase>(new RTreeRecord(middle, 2))); + Rect bottom(6, 6, 4, 4); + parent->AddChild(scoped_ptr<RTreeNodeBase>(new RTreeRecord(bottom, 3))); + ValidateNode(parent.get(), 1U, 5U); + + // Test a rect in corner. + Rect corner(0, 0, 1, 1); + Rect expanded = top; + expanded.Union(corner); + // It should not add any overlap to add this to the first child at (0, 0). + EXPECT_EQ(0, NodeOverlapIncreaseToAdd( + parent.get(), corner, parent->child(0), expanded)); + + expanded = middle; + expanded.Union(corner); + // Overlap for middle rectangle should increase from 2 pixels at (3, 3) and + // (6, 6) to 17 pixels, as it will now cover 4x4 rectangle top, + // so a change of +15. + EXPECT_EQ(15, NodeOverlapIncreaseToAdd( + parent.get(), corner, parent->child(1), expanded)); + + expanded = bottom; + expanded.Union(corner); + // Overlap for bottom rectangle should increase from 1 pixel at (6, 6) to + // 32 pixels, as it will now cover both 4x4 rectangles top and middle, + // so a change of 31. + EXPECT_EQ(31, NodeOverlapIncreaseToAdd( + parent.get(), corner, parent->child(2), expanded)); + + // Test a rect that doesn't overlap with anything, in the far right corner. + Rect far_corner(9, 0, 1, 1); + expanded = top; + expanded.Union(far_corner); + // Overlap of top should go from 1 to 4, as it will now cover the entire first + // row of pixels in middle. + EXPECT_EQ(3, NodeOverlapIncreaseToAdd( + parent.get(), far_corner, parent->child(0), expanded)); + + expanded = middle; + expanded.Union(far_corner); + // Overlap of middle should go from 2 to 8, as it will cover the rightmost 4 + // pixels of top and the top 4 pixels of bottom as it expands. + EXPECT_EQ(6, NodeOverlapIncreaseToAdd( + parent.get(), far_corner, parent->child(1), expanded)); + + expanded = bottom; + expanded.Union(far_corner); + // Overlap of bottom should go from 1 to 4, as it will now cover the rightmost + // 4 pixels of middle. + EXPECT_EQ(3, NodeOverlapIncreaseToAdd( + parent.get(), far_corner, parent->child(2), expanded)); +} + +TEST_F(RTreeNodeTest, BuildLowBounds) { + RTreeNodes records; + records.reserve(10); + for (int i = 1; i <= 10; ++i) + records.push_back(new RTreeRecord(Rect(0, 0, i, i), i)); + + RTreeRects vertical_bounds; + RTreeRects horizontal_bounds; + NodeBuildLowBounds( + records.get(), records.get(), &vertical_bounds, &horizontal_bounds); + for (int i = 0; i < 10; ++i) { + EXPECT_EQ(records[i]->rect(), vertical_bounds[i]); + EXPECT_EQ(records[i]->rect(), horizontal_bounds[i]); + } +} + +TEST_F(RTreeNodeTest, BuildHighBounds) { + RTreeNodes records; + records.reserve(25); + for (int i = 0; i < 25; ++i) + records.push_back(new RTreeRecord(Rect(i, i, 25 - i, 25 - i), i)); + + RTreeRects vertical_bounds; + RTreeRects horizontal_bounds; + NodeBuildHighBounds( + records.get(), records.get(), &vertical_bounds, &horizontal_bounds); + for (int i = 0; i < 25; ++i) { + EXPECT_EQ(records[i]->rect(), vertical_bounds[i]); + EXPECT_EQ(records[i]->rect(), horizontal_bounds[i]); + } +} + +TEST_F(RTreeNodeTest, ChooseSplitAxisAndIndexVertical) { + RTreeRects low_vertical_bounds; + RTreeRects high_vertical_bounds; + RTreeRects low_horizontal_bounds; + RTreeRects high_horizontal_bounds; + // In this test scenario we describe a mirrored, stacked configuration of + // horizontal, 1 pixel high rectangles labeled a-f like this: + // + // shape: | v sort: | h sort: | + // -------+---------+---------+ + // aaaaa | 0 | 0 | + // bbb | 1 | 2 | + // c | 2 | 4 | + // d | 3 | 5 | + // eee | 4 | 3 | + // fffff | 5 | 1 | + // + // These are already sorted vertically from top to bottom. Bounding rectangles + // of these vertically sorted will be 5 wide, i tall bounding boxes. + for (int i = 0; i < 6; ++i) { + low_vertical_bounds.push_back(Rect(0, 0, 5, i + 1)); + high_vertical_bounds.push_back(Rect(0, i, 5, 6 - i)); + } + + // Low bounds of horizontal sort start with bounds of box a and then jump to + // cover everything, as box f is second in horizontal sort. + low_horizontal_bounds.push_back(Rect(0, 0, 5, 1)); + for (int i = 0; i < 5; ++i) + low_horizontal_bounds.push_back(Rect(0, 0, 5, 6)); + + // High horizontal bounds are hand-calculated. + high_horizontal_bounds.push_back(Rect(0, 0, 5, 6)); + high_horizontal_bounds.push_back(Rect(0, 1, 5, 5)); + high_horizontal_bounds.push_back(Rect(1, 1, 3, 4)); + high_horizontal_bounds.push_back(Rect(1, 2, 3, 3)); + high_horizontal_bounds.push_back(Rect(2, 2, 1, 2)); + high_horizontal_bounds.push_back(Rect(2, 3, 1, 1)); + + int smallest_vertical_margin = + NodeSmallestMarginSum(2, 3, low_vertical_bounds, high_vertical_bounds); + int smallest_horizontal_margin = NodeSmallestMarginSum( + 2, 3, low_horizontal_bounds, high_horizontal_bounds); + EXPECT_LT(smallest_vertical_margin, smallest_horizontal_margin); + + EXPECT_EQ( + 3U, + NodeChooseSplitIndex(2, 5, low_vertical_bounds, high_vertical_bounds)); +} + +TEST_F(RTreeNodeTest, ChooseSplitAxisAndIndexHorizontal) { + RTreeRects low_vertical_bounds; + RTreeRects high_vertical_bounds; + RTreeRects low_horizontal_bounds; + RTreeRects high_horizontal_bounds; + // We rotate the shape from ChooseSplitAxisAndIndexVertical to test + // horizontal split axis detection: + // + // +--------+ + // | a f | + // | ab ef | + // sort: | abcdef | + // | ab ef | + // | a f | + // |--------+ + // v sort: | 024531 | + // h sort: | 012345 | + // +--------+ + // + // Low bounds of vertical sort start with bounds of box a and then jump to + // cover everything, as box f is second in vertical sort. + low_vertical_bounds.push_back(Rect(0, 0, 1, 5)); + for (int i = 0; i < 5; ++i) + low_vertical_bounds.push_back(Rect(0, 0, 6, 5)); + + // High vertical bounds are hand-calculated. + high_vertical_bounds.push_back(Rect(0, 0, 6, 5)); + high_vertical_bounds.push_back(Rect(1, 0, 5, 5)); + high_vertical_bounds.push_back(Rect(1, 1, 4, 3)); + high_vertical_bounds.push_back(Rect(2, 1, 3, 3)); + high_vertical_bounds.push_back(Rect(2, 2, 2, 1)); + high_vertical_bounds.push_back(Rect(3, 2, 1, 1)); + + // These are already sorted horizontally from left to right. Bounding + // rectangles of these horizontally sorted will be i wide, 5 tall bounding + // boxes. + for (int i = 0; i < 6; ++i) { + low_horizontal_bounds.push_back(Rect(0, 0, i + 1, 5)); + high_horizontal_bounds.push_back(Rect(i, 0, 6 - i, 5)); + } + + int smallest_vertical_margin = + NodeSmallestMarginSum(2, 3, low_vertical_bounds, high_vertical_bounds); + int smallest_horizontal_margin = NodeSmallestMarginSum( + 2, 3, low_horizontal_bounds, high_horizontal_bounds); + + EXPECT_GT(smallest_vertical_margin, smallest_horizontal_margin); + + EXPECT_EQ(3U, + NodeChooseSplitIndex( + 2, 5, low_horizontal_bounds, high_horizontal_bounds)); +} + +TEST_F(RTreeNodeTest, DivideChildren) { + // Create a test node to split. + scoped_ptr<RTreeNode> test_node(new RTreeNode); + std::vector<RTreeNodeBase*> sorted_children; + RTreeRects low_bounds; + RTreeRects high_bounds; + // Insert 10 record nodes, also inserting them into our children array. + for (int i = 1; i <= 10; ++i) { + scoped_ptr<RTreeRecord> record(new RTreeRecord(Rect(0, 0, i, i), i)); + sorted_children.push_back(record.get()); + test_node->AddChild(record.PassAs<RTreeNodeBase>()); + low_bounds.push_back(Rect(0, 0, i, i)); + high_bounds.push_back(Rect(0, 0, 10, 10)); + } + // Split the children in half. + scoped_ptr<RTreeNodeBase> split_node_base(NodeDivideChildren( + test_node.get(), low_bounds, high_bounds, sorted_children, 5)); + RTreeNode* split_node = static_cast<RTreeNode*>(split_node_base.get()); + // Both nodes should be valid. + ValidateNode(test_node.get(), 1U, 10U); + ValidateNode(split_node, 1U, 10U); + // Both nodes should have five children. + EXPECT_EQ(5U, test_node->count()); + EXPECT_EQ(5U, split_node->count()); + // Test node should have children 1-5, split node should have children 6-10. + for (int i = 0; i < 5; ++i) { + EXPECT_EQ(i + 1, record(test_node.get(), i)->key()); + EXPECT_EQ(i + 6, record(split_node, i)->key()); + } +} + +TEST_F(RTreeNodeTest, RemoveChildNoOrphans) { + scoped_ptr<RTreeNode> test_parent(new RTreeNode); + test_parent->AddChild( + scoped_ptr<RTreeNodeBase>(new RTreeRecord(Rect(0, 0, 1, 1), 1))); + test_parent->AddChild( + scoped_ptr<RTreeNodeBase>(new RTreeRecord(Rect(0, 0, 2, 2), 2))); + test_parent->AddChild( + scoped_ptr<RTreeNodeBase>(new RTreeRecord(Rect(0, 0, 3, 3), 3))); + ValidateNode(test_parent.get(), 1U, 5U); + + RTreeNodes orphans; + + // Remove the middle node. + scoped_ptr<RTreeNodeBase> middle_child( + test_parent->RemoveChild(test_parent->child(1), &orphans)); + EXPECT_EQ(0U, orphans.size()); + EXPECT_EQ(2U, test_parent->count()); + NodeRecomputeLocalBounds(test_parent.get()); + ValidateNode(test_parent.get(), 1U, 5U); + + // Remove the end node. + scoped_ptr<RTreeNodeBase> end_child( + test_parent->RemoveChild(test_parent->child(1), &orphans)); + EXPECT_EQ(0U, orphans.size()); + EXPECT_EQ(1U, test_parent->count()); + NodeRecomputeLocalBounds(test_parent.get()); + ValidateNode(test_parent.get(), 1U, 5U); + + // Remove the first node. + scoped_ptr<RTreeNodeBase> first_child( + test_parent->RemoveChild(test_parent->child(0), &orphans)); + EXPECT_EQ(0U, orphans.size()); + EXPECT_EQ(0U, test_parent->count()); +} + +TEST_F(RTreeNodeTest, RemoveChildOrphans) { + // Build binary tree of Nodes of height 4, keeping weak pointers to the + // Levels 0 and 1 Nodes and the Records so we can test removal of them below. + std::vector<RTreeNode*> level_1_children; + std::vector<RTreeNode*> level_0_children; + std::vector<RTreeRecord*> records; + int id = 1; + scoped_ptr<RTreeNode> root(NewNodeAtLevel(2)); + for (int i = 0; i < 2; ++i) { + scoped_ptr<RTreeNode> level_1_child(NewNodeAtLevel(1)); + for (int j = 0; j < 2; ++j) { + scoped_ptr<RTreeNode> level_0_child(new RTreeNode); + for (int k = 0; k < 2; ++k) { + scoped_ptr<RTreeRecord> record( + new RTreeRecord(Rect(0, 0, id, id), id)); + ++id; + records.push_back(record.get()); + level_0_child->AddChild(record.PassAs<RTreeNodeBase>()); + } + level_0_children.push_back(level_0_child.get()); + level_1_child->AddChild(level_0_child.PassAs<RTreeNodeBase>()); + } + level_1_children.push_back(level_1_child.get()); + root->AddChild(level_1_child.PassAs<RTreeNodeBase>()); + } + + // This should now be a valid tree structure. + ValidateNode(root.get(), 2U, 2U); + EXPECT_EQ(2U, level_1_children.size()); + EXPECT_EQ(4U, level_0_children.size()); + EXPECT_EQ(8U, records.size()); + + // Now remove all of the level 0 nodes so we get the record nodes as orphans. + RTreeNodes orphans; + for (size_t i = 0; i < level_0_children.size(); ++i) + level_1_children[i / 2]->RemoveChild(level_0_children[i], &orphans); + + // Orphans should be all 8 records but no order guarantee. + EXPECT_EQ(8U, orphans.size()); + for (std::vector<RTreeRecord*>::iterator it = records.begin(); + it != records.end(); ++it) { + RTreeNodes::iterator orphan = + std::find(orphans.begin(), orphans.end(), *it); + EXPECT_NE(orphan, orphans.end()); + orphans.erase(orphan); + } + EXPECT_EQ(0U, orphans.size()); +} + +TEST_F(RTreeNodeTest, RemoveAndReturnLastChild) { + scoped_ptr<RTreeNode> test_parent(new RTreeNode); + test_parent->AddChild( + scoped_ptr<RTreeNodeBase>(new RTreeRecord(Rect(0, 0, 1, 1), 1))); + test_parent->AddChild( + scoped_ptr<RTreeNodeBase>(new RTreeRecord(Rect(0, 0, 2, 2), 2))); + test_parent->AddChild( + scoped_ptr<RTreeNodeBase>(new RTreeRecord(Rect(0, 0, 3, 3), 3))); + ValidateNode(test_parent.get(), 1U, 5U); + + RTreeNodeBase* child = test_parent->child(2); + scoped_ptr<RTreeNodeBase> last_child(test_parent->RemoveAndReturnLastChild()); + EXPECT_EQ(child, last_child.get()); + EXPECT_EQ(2U, test_parent->count()); + NodeRecomputeLocalBounds(test_parent.get()); + ValidateNode(test_parent.get(), 1U, 5U); + + child = test_parent->child(1); + scoped_ptr<RTreeNodeBase> middle_child( + test_parent->RemoveAndReturnLastChild()); + EXPECT_EQ(child, middle_child.get()); + EXPECT_EQ(1U, test_parent->count()); + NodeRecomputeLocalBounds(test_parent.get()); + ValidateNode(test_parent.get(), 1U, 5U); + + child = test_parent->child(0); + scoped_ptr<RTreeNodeBase> first_child( + test_parent->RemoveAndReturnLastChild()); + EXPECT_EQ(child, first_child.get()); + EXPECT_EQ(0U, test_parent->count()); +} + +TEST_F(RTreeNodeTest, LeastOverlapIncrease) { + scoped_ptr<RTreeNode> test_parent(NewNodeAtLevel(1)); + // Construct 4 nodes with 1x2 rects spaced horizontally 1 pixel apart, or: + // + // a b c d + // a b c d + // + for (int i = 0; i < 4; ++i) { + scoped_ptr<RTreeNode> node(new RTreeNode); + scoped_ptr<RTreeRecord> record( + new RTreeRecord(Rect(i * 2, 0, 1, 2), i + 1)); + node->AddChild(record.PassAs<RTreeNodeBase>()); + test_parent->AddChild(node.PassAs<RTreeNodeBase>()); + } + + ValidateNode(test_parent.get(), 1U, 5U); + + // Test rect at (7, 0) should require minimum overlap on the part of the + // fourth rectangle to add: + // + // a b c dT + // a b c d + // + Rect test_rect_far(7, 0, 1, 1); + RTreeRects expanded_rects; + BuildExpandedRects(test_parent.get(), test_rect_far, &expanded_rects); + RTreeNode* result = NodeLeastOverlapIncrease( + test_parent.get(), test_rect_far, expanded_rects); + EXPECT_EQ(4, record(result, 0)->key()); + + // Test rect covering the bottom half of all children should be a 4-way tie, + // so LeastOverlapIncrease should return NULL: + // + // a b c d + // TTTTTTT + // + Rect test_rect_tie(0, 1, 7, 1); + BuildExpandedRects(test_parent.get(), test_rect_tie, &expanded_rects); + result = NodeLeastOverlapIncrease( + test_parent.get(), test_rect_tie, expanded_rects); + EXPECT_TRUE(result == NULL); + + // Test rect completely inside c should return the third rectangle: + // + // a b T d + // a b c d + // + Rect test_rect_inside(4, 0, 1, 1); + BuildExpandedRects(test_parent.get(), test_rect_inside, &expanded_rects); + result = NodeLeastOverlapIncrease( + test_parent.get(), test_rect_inside, expanded_rects); + EXPECT_EQ(3, record(result, 0)->key()); + + // Add a rectangle that overlaps completely with rectangle c, to test + // when there is a tie between two completely contained rectangles: + // + // a b Ted + // a b eed + // + scoped_ptr<RTreeNode> record_parent(new RTreeNode); + record_parent->AddChild( + scoped_ptr<RTreeNodeBase>(new RTreeRecord(Rect(4, 0, 2, 2), 9))); + test_parent->AddChild(record_parent.PassAs<RTreeNodeBase>()); + BuildExpandedRects(test_parent.get(), test_rect_inside, &expanded_rects); + result = NodeLeastOverlapIncrease( + test_parent.get(), test_rect_inside, expanded_rects); + EXPECT_TRUE(result == NULL); +} + +TEST_F(RTreeNodeTest, LeastAreaEnlargement) { + scoped_ptr<RTreeNode> test_parent(NewNodeAtLevel(1)); + // Construct 4 nodes in a cross-hairs style configuration: + // + // a + // b c + // d + // + scoped_ptr<RTreeNode> node(new RTreeNode); + node->AddChild( + scoped_ptr<RTreeNodeBase>(new RTreeRecord(Rect(1, 0, 1, 1), 1))); + test_parent->AddChild(node.PassAs<RTreeNodeBase>()); + node.reset(new RTreeNode); + node->AddChild( + scoped_ptr<RTreeNodeBase>(new RTreeRecord(Rect(0, 1, 1, 1), 2))); + test_parent->AddChild(node.PassAs<RTreeNodeBase>()); + node.reset(new RTreeNode); + node->AddChild( + scoped_ptr<RTreeNodeBase>(new RTreeRecord(Rect(2, 1, 1, 1), 3))); + test_parent->AddChild(node.PassAs<RTreeNodeBase>()); + node.reset(new RTreeNode); + node->AddChild( + scoped_ptr<RTreeNodeBase>(new RTreeRecord(Rect(1, 2, 1, 1), 4))); + test_parent->AddChild(node.PassAs<RTreeNodeBase>()); + + ValidateNode(test_parent.get(), 1U, 5U); + + // Test rect at (1, 3) should require minimum area to add to Node d: + // + // a + // b c + // d + // T + // + Rect test_rect_below(1, 3, 1, 1); + RTreeRects expanded_rects; + BuildExpandedRects(test_parent.get(), test_rect_below, &expanded_rects); + RTreeNode* result = NodeLeastAreaEnlargement( + test_parent.get(), test_rect_below, expanded_rects); + EXPECT_EQ(4, record(result, 0)->key()); + + // Test rect completely inside b should require minimum area to add to Node b: + // + // a + // T c + // d + // + Rect test_rect_inside(0, 1, 1, 1); + BuildExpandedRects(test_parent.get(), test_rect_inside, &expanded_rects); + result = NodeLeastAreaEnlargement( + test_parent.get(), test_rect_inside, expanded_rects); + EXPECT_EQ(2, record(result, 0)->key()); + + // Add e at (0, 1) to overlap b and c, to test tie-breaking: + // + // a + // eee + // d + // + node.reset(new RTreeNode); + node->AddChild( + scoped_ptr<RTreeNodeBase>(new RTreeRecord(Rect(0, 1, 3, 1), 7))); + test_parent->AddChild(node.PassAs<RTreeNodeBase>()); + + ValidateNode(test_parent.get(), 1U, 5U); + + // Test rect at (3, 1) should tie between c and e, but c has smaller area so + // the algorithm should select c: + // + // + // a + // eeeT + // d + // + Rect test_rect_tie_breaker(3, 1, 1, 1); + BuildExpandedRects(test_parent.get(), test_rect_tie_breaker, &expanded_rects); + result = NodeLeastAreaEnlargement( + test_parent.get(), test_rect_tie_breaker, expanded_rects); + EXPECT_EQ(3, record(result, 0)->key()); +} + +// RTreeTest ------------------------------------------------------------------ + +// An empty RTree should never return AppendIntersectingRecords results, and +// RTrees should be empty upon construction. +TEST_F(RTreeTest, AppendIntersectingRecordsOnEmptyTree) { + RT rt(2, 10); + ValidateRTree(&rt); + RT::Matches results; + Rect test_rect(25, 25); + rt.AppendIntersectingRecords(test_rect, &results); + EXPECT_EQ(0U, results.size()); + ValidateRTree(&rt); +} + +// Clear should empty the tree, meaning that all queries should not return +// results after. +TEST_F(RTreeTest, ClearEmptiesTreeOfSingleNode) { + RT rt(2, 5); + rt.Insert(Rect(0, 0, 100, 100), 1); + rt.Clear(); + RT::Matches results; + Rect test_rect(1, 1); + rt.AppendIntersectingRecords(test_rect, &results); + EXPECT_EQ(0U, results.size()); + ValidateRTree(&rt); +} + +// Even with a complex internal structure, clear should empty the tree, meaning +// that all queries should not return results after. +TEST_F(RTreeTest, ClearEmptiesTreeOfManyNodes) { + RT rt(2, 5); + AddStackedSquares(&rt, 100); + rt.Clear(); + RT::Matches results; + Rect test_rect(1, 1); + rt.AppendIntersectingRecords(test_rect, &results); + EXPECT_EQ(0U, results.size()); + ValidateRTree(&rt); +} + +// Duplicate inserts should overwrite previous inserts. +TEST_F(RTreeTest, DuplicateInsertsOverwrite) { + RT rt(2, 5); + // Add 100 stacked squares, but always with duplicate key of 0. + for (int i = 1; i <= 100; ++i) { + rt.Insert(Rect(0, 0, i, i), 0); + ValidateRTree(&rt); + } + RT::Matches results; + Rect test_rect(1, 1); + rt.AppendIntersectingRecords(test_rect, &results); + EXPECT_EQ(1U, results.size()); + EXPECT_EQ(1U, results.count(0)); +} + +// Call Remove() once on something that's been inserted repeatedly. +TEST_F(RTreeTest, DuplicateInsertRemove) { + RT rt(3, 9); + AddStackedSquares(&rt, 25); + for (int i = 1; i <= 100; ++i) { + rt.Insert(Rect(0, 0, i, i), 26); + ValidateRTree(&rt); + } + rt.Remove(26); + RT::Matches results; + Rect test_rect(1, 1); + rt.AppendIntersectingRecords(test_rect, &results); + EXPECT_EQ(25U, results.size()); + VerifyAllKeys(results); +} + +// Call Remove() repeatedly on something that's been inserted once. +TEST_F(RTreeTest, InsertDuplicateRemove) { + RT rt(7, 15); + AddStackedSquares(&rt, 101); + for (int i = 0; i < 100; ++i) { + rt.Remove(101); + ValidateRTree(&rt); + } + RT::Matches results; + Rect test_rect(1, 1); + rt.AppendIntersectingRecords(test_rect, &results); + EXPECT_EQ(100U, results.size()); + VerifyAllKeys(results); +} + +// Stacked rects should meet all matching queries regardless of nesting. +TEST_F(RTreeTest, AppendIntersectingRecordsStackedSquaresNestedHit) { + RT rt(2, 5); + AddStackedSquares(&rt, 100); + RT::Matches results; + Rect test_rect(1, 1); + rt.AppendIntersectingRecords(test_rect, &results); + EXPECT_EQ(100U, results.size()); + VerifyAllKeys(results); +} + +// Stacked rects should meet all matching queries when contained completely by +// the query rectangle. +TEST_F(RTreeTest, AppendIntersectingRecordsStackedSquaresContainedHit) { + RT rt(2, 10); + AddStackedSquares(&rt, 100); + RT::Matches results; + Rect test_rect(0, 0, 100, 100); + rt.AppendIntersectingRecords(test_rect, &results); + EXPECT_EQ(100U, results.size()); + VerifyAllKeys(results); +} + +// Stacked rects should miss a missing query when the query has no intersection +// with the rects. +TEST_F(RTreeTest, AppendIntersectingRecordsStackedSquaresCompleteMiss) { + RT rt(2, 7); + AddStackedSquares(&rt, 100); + RT::Matches results; + Rect test_rect(150, 150, 100, 100); + rt.AppendIntersectingRecords(test_rect, &results); + EXPECT_EQ(0U, results.size()); +} + +// Removing half the nodes after insertion should still result in a valid tree. +TEST_F(RTreeTest, RemoveHalfStackedRects) { + RT rt(2, 11); + AddStackedSquares(&rt, 200); + for (int i = 101; i <= 200; ++i) { + rt.Remove(i); + ValidateRTree(&rt); + } + RT::Matches results; + Rect test_rect(1, 1); + rt.AppendIntersectingRecords(test_rect, &results); + EXPECT_EQ(100U, results.size()); + VerifyAllKeys(results); + + // Add the nodes back in. + for (int i = 101; i <= 200; ++i) { + rt.Insert(Rect(0, 0, i, i), i); + ValidateRTree(&rt); + } + results.clear(); + rt.AppendIntersectingRecords(test_rect, &results); + EXPECT_EQ(200U, results.size()); + VerifyAllKeys(results); +} + +TEST_F(RTreeTest, InsertDupToRoot) { + RT rt(2, 5); + rt.Insert(Rect(0, 0, 1, 2), 1); + ValidateRTree(&rt); + rt.Insert(Rect(0, 0, 2, 1), 1); + ValidateRTree(&rt); +} + +TEST_F(RTreeTest, InsertNegativeCoordsRect) { + RT rt(5, 11); + for (int i = 1; i <= 100; ++i) { + rt.Insert(Rect(-i, -i, i, i), (i * 2) - 1); + ValidateRTree(&rt); + rt.Insert(Rect(0, 0, i, i), i * 2); + ValidateRTree(&rt); + } + RT::Matches results; + Rect test_rect(-1, -1, 2, 2); + rt.AppendIntersectingRecords(test_rect, &results); + EXPECT_EQ(200U, results.size()); + VerifyAllKeys(results); +} + +TEST_F(RTreeTest, RemoveNegativeCoordsRect) { + RT rt(7, 21); + + // Add 100 positive stacked squares. + AddStackedSquares(&rt, 100); + + // Now add 100 negative stacked squares. + for (int i = 101; i <= 200; ++i) { + rt.Insert(Rect(100 - i, 100 - i, i - 100, i - 100), 301 - i); + ValidateRTree(&rt); + } + + // Now remove half of the negative squares. + for (int i = 101; i <= 150; ++i) { + rt.Remove(301 - i); + ValidateRTree(&rt); + } + + // Queries should return 100 positive and 50 negative stacked squares. + RT::Matches results; + Rect test_rect(-1, -1, 2, 2); + rt.AppendIntersectingRecords(test_rect, &results); + EXPECT_EQ(150U, results.size()); + VerifyAllKeys(results); +} + +TEST_F(RTreeTest, InsertEmptyRectReplacementRemovesKey) { + RT rt(10, 31); + AddStackedSquares(&rt, 50); + ValidateRTree(&rt); + + // Replace last square with empty rect. + rt.Insert(Rect(), 50); + ValidateRTree(&rt); + + // Now query large area to get all rects in tree. + RT::Matches results; + Rect test_rect(0, 0, 100, 100); + rt.AppendIntersectingRecords(test_rect, &results); + + // Should only be 49 rects in tree. + EXPECT_EQ(49U, results.size()); + VerifyAllKeys(results); +} + +TEST_F(RTreeTest, InsertReplacementMaintainsTree) { + RT rt(2, 5); + AddStackedSquares(&rt, 100); + ValidateRTree(&rt); + + for (int i = 1; i <= 100; ++i) { + rt.Insert(Rect(0, 0, 0, 0), i); + ValidateRTree(&rt); + } +} + +} // namespace gfx diff --git a/chromium/ui/gfx/rect.cc b/chromium/ui/gfx/geometry/rect.cc index 8372cc4e7c5..f418e175b89 100644 --- a/chromium/ui/gfx/rect.cc +++ b/chromium/ui/gfx/geometry/rect.cc @@ -2,20 +2,18 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ui/gfx/rect.h" +#include "ui/gfx/geometry/rect.h" #include <algorithm> #if defined(OS_WIN) #include <windows.h> -#elif defined(TOOLKIT_GTK) -#include <gdk/gdk.h> #endif #include "base/logging.h" #include "base/strings/stringprintf.h" -#include "ui/gfx/insets.h" -#include "ui/gfx/rect_base_impl.h" +#include "ui/gfx/geometry/insets.h" +#include "ui/gfx/geometry/rect_base_impl.h" namespace gfx { @@ -35,12 +33,6 @@ Rect::Rect(const CGRect& r) set_width(r.size.width); set_height(r.size.height); } -#elif defined(TOOLKIT_GTK) -Rect::Rect(const GdkRectangle& r) - : RectBaseT(gfx::Point(r.x, r.y)) { - set_width(r.width); - set_height(r.height); -} #endif #if defined(OS_WIN) @@ -56,11 +48,6 @@ RECT Rect::ToRECT() const { CGRect Rect::ToCGRect() const { return CGRectMake(x(), y(), width(), height()); } -#elif defined(TOOLKIT_GTK) -GdkRectangle Rect::ToGdkRectangle() const { - GdkRectangle r = {x(), y(), width(), height()}; - return r; -} #endif std::string Rect::ToString() const { diff --git a/chromium/ui/gfx/geometry/rect.h b/chromium/ui/gfx/geometry/rect.h new file mode 100644 index 00000000000..28d37bc31b3 --- /dev/null +++ b/chromium/ui/gfx/geometry/rect.h @@ -0,0 +1,139 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Defines a simple integer rectangle class. The containment semantics +// are array-like; that is, the coordinate (x, y) is considered to be +// contained by the rectangle, but the coordinate (x + width, y) is not. +// The class will happily let you create malformed rectangles (that is, +// rectangles with negative width and/or height), but there will be assertions +// in the operations (such as Contains()) to complain in this case. + +#ifndef UI_GFX_GEOMETRY_RECT_H_ +#define UI_GFX_GEOMETRY_RECT_H_ + +#include <cmath> +#include <string> + +#include "ui/gfx/geometry/point.h" +#include "ui/gfx/geometry/rect_base.h" +#include "ui/gfx/geometry/rect_f.h" +#include "ui/gfx/geometry/size.h" +#include "ui/gfx/geometry/vector2d.h" + +#if defined(OS_WIN) +typedef struct tagRECT RECT; +#elif defined(OS_IOS) +#include <CoreGraphics/CoreGraphics.h> +#elif defined(OS_MACOSX) +#include <ApplicationServices/ApplicationServices.h> +#endif + +namespace gfx { + +class Insets; + +class GFX_EXPORT Rect + : public RectBase<Rect, Point, Size, Insets, Vector2d, int> { + public: + Rect() : RectBase<Rect, Point, Size, Insets, Vector2d, int>(Point()) {} + + Rect(int width, int height) + : RectBase<Rect, Point, Size, Insets, Vector2d, int> + (Size(width, height)) {} + + Rect(int x, int y, int width, int height) + : RectBase<Rect, Point, Size, Insets, Vector2d, int> + (Point(x, y), Size(width, height)) {} + +#if defined(OS_WIN) + explicit Rect(const RECT& r); +#elif defined(OS_MACOSX) + explicit Rect(const CGRect& r); +#endif + + explicit Rect(const gfx::Size& size) + : RectBase<Rect, Point, Size, Insets, Vector2d, int>(size) {} + + Rect(const gfx::Point& origin, const gfx::Size& size) + : RectBase<Rect, Point, Size, Insets, Vector2d, int>(origin, size) {} + + ~Rect() {} + +#if defined(OS_WIN) + // Construct an equivalent Win32 RECT object. + RECT ToRECT() const; +#elif defined(OS_MACOSX) + // Construct an equivalent CoreGraphics object. + CGRect ToCGRect() const; +#endif + + operator RectF() const { + return RectF(origin().x(), origin().y(), size().width(), size().height()); + } + + std::string ToString() const; +}; + +inline bool operator==(const Rect& lhs, const Rect& rhs) { + return lhs.origin() == rhs.origin() && lhs.size() == rhs.size(); +} + +inline bool operator!=(const Rect& lhs, const Rect& rhs) { + return !(lhs == rhs); +} + +GFX_EXPORT Rect operator+(const Rect& lhs, const Vector2d& rhs); +GFX_EXPORT Rect operator-(const Rect& lhs, const Vector2d& rhs); + +inline Rect operator+(const Vector2d& lhs, const Rect& rhs) { + return rhs + lhs; +} + +GFX_EXPORT Rect IntersectRects(const Rect& a, const Rect& b); +GFX_EXPORT Rect UnionRects(const Rect& a, const Rect& b); +GFX_EXPORT Rect SubtractRects(const Rect& a, const Rect& b); + +// Constructs a rectangle with |p1| and |p2| as opposite corners. +// +// This could also be thought of as "the smallest rect that contains both +// points", except that we consider points on the right/bottom edges of the +// rect to be outside the rect. So technically one or both points will not be +// contained within the rect, because they will appear on one of these edges. +GFX_EXPORT Rect BoundingRect(const Point& p1, const Point& p2); + +inline Rect ScaleToEnclosingRect(const Rect& rect, + float x_scale, + float y_scale) { + int x = std::floor(rect.x() * x_scale); + int y = std::floor(rect.y() * y_scale); + int r = rect.width() == 0 ? x : std::ceil(rect.right() * x_scale); + int b = rect.height() == 0 ? y : std::ceil(rect.bottom() * y_scale); + return Rect(x, y, r - x, b - y); +} + +inline Rect ScaleToEnclosingRect(const Rect& rect, float scale) { + return ScaleToEnclosingRect(rect, scale, scale); +} + +inline Rect ScaleToEnclosedRect(const Rect& rect, + float x_scale, + float y_scale) { + int x = std::ceil(rect.x() * x_scale); + int y = std::ceil(rect.y() * y_scale); + int r = rect.width() == 0 ? x : std::floor(rect.right() * x_scale); + int b = rect.height() == 0 ? y : std::floor(rect.bottom() * y_scale); + return Rect(x, y, r - x, b - y); +} + +inline Rect ScaleToEnclosedRect(const Rect& rect, float scale) { + return ScaleToEnclosedRect(rect, scale, scale); +} + +#if !defined(COMPILER_MSVC) +extern template class RectBase<Rect, Point, Size, Insets, Vector2d, int>; +#endif + +} // namespace gfx + +#endif // UI_GFX_GEOMETRY_RECT_H_ diff --git a/chromium/ui/gfx/rect_base.h b/chromium/ui/gfx/geometry/rect_base.h index 412836ced19..b0a9b06a6b5 100644 --- a/chromium/ui/gfx/rect_base.h +++ b/chromium/ui/gfx/geometry/rect_base.h @@ -9,8 +9,8 @@ // rectangles with negative width and/or height), but there will be assertions // in the operations (such as Contains()) to complain in this case. -#ifndef UI_GFX_RECT_BASE_H_ -#define UI_GFX_RECT_BASE_H_ +#ifndef UI_GFX_GEOMETRY_RECT_BASE_H_ +#define UI_GFX_GEOMETRY_RECT_BASE_H_ #include <string> @@ -171,4 +171,4 @@ class GFX_EXPORT RectBase { } // namespace gfx -#endif // UI_GFX_RECT_BASE_H_ +#endif // UI_GFX_GEOMETRY_RECT_BASE_H_ diff --git a/chromium/ui/gfx/rect_base_impl.h b/chromium/ui/gfx/geometry/rect_base_impl.h index 52201691a1b..7720608e9b5 100644 --- a/chromium/ui/gfx/rect_base_impl.h +++ b/chromium/ui/gfx/geometry/rect_base_impl.h @@ -4,10 +4,9 @@ #include <limits> -#include "ui/gfx/rect_base.h" - #include "base/logging.h" #include "base/strings/stringprintf.h" +#include "ui/gfx/geometry/rect_base.h" // This file provides the implementation for RectBaese template and // used to instantiate the base class for Rect and RectF classes. diff --git a/chromium/ui/gfx/rect_conversions.cc b/chromium/ui/gfx/geometry/rect_conversions.cc index a54a49a23d6..cceeb83a0ae 100644 --- a/chromium/ui/gfx/rect_conversions.cc +++ b/chromium/ui/gfx/geometry/rect_conversions.cc @@ -2,13 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ui/gfx/rect_conversions.h" +#include "ui/gfx/geometry/rect_conversions.h" #include <algorithm> #include <cmath> #include "base/logging.h" -#include "ui/gfx/safe_integer_conversions.h" +#include "ui/gfx/geometry/safe_integer_conversions.h" namespace gfx { diff --git a/chromium/ui/gfx/geometry/rect_conversions.h b/chromium/ui/gfx/geometry/rect_conversions.h new file mode 100644 index 00000000000..617074abeee --- /dev/null +++ b/chromium/ui/gfx/geometry/rect_conversions.h @@ -0,0 +1,36 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_GFX_GEOMETRY_RECT_CONVERSIONS_H_ +#define UI_GFX_GEOMETRY_RECT_CONVERSIONS_H_ + +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/rect_f.h" + +namespace gfx { + +// Returns the smallest Rect that encloses the given RectF. +GFX_EXPORT Rect ToEnclosingRect(const RectF& rect); + +// Returns the largest Rect that is enclosed by the given RectF. +GFX_EXPORT Rect ToEnclosedRect(const RectF& rect); + +// Returns the Rect after snapping the corners of the RectF to an integer grid. +// This should only be used when the RectF you provide is expected to be an +// integer rect with floating point error. If it is an arbitrary RectF, then +// you should use a different method. +GFX_EXPORT Rect ToNearestRect(const RectF& rect); + +// Returns true if the Rect produced after snapping the corners of the RectF +// to an integer grid is withing |distance|. +GFX_EXPORT bool IsNearestRectWithinDistance( + const gfx::RectF& rect, float distance); + +// Returns a Rect obtained by flooring the values of the given RectF. +// Please prefer the previous two functions in new code. +GFX_EXPORT Rect ToFlooredRectDeprecated(const RectF& rect); + +} // namespace gfx + +#endif // UI_GFX_GEOMETRY_RECT_CONVERSIONS_H_ diff --git a/chromium/ui/gfx/rect_f.cc b/chromium/ui/gfx/geometry/rect_f.cc index c55752aa843..44c08aafaff 100644 --- a/chromium/ui/gfx/rect_f.cc +++ b/chromium/ui/gfx/geometry/rect_f.cc @@ -2,15 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ui/gfx/rect_f.h" +#include "ui/gfx/geometry/rect_f.h" #include <algorithm> #include "base/logging.h" #include "base/strings/stringprintf.h" -#include "ui/gfx/insets_f.h" -#include "ui/gfx/rect_base_impl.h" -#include "ui/gfx/safe_integer_conversions.h" +#include "ui/gfx/geometry/insets_f.h" +#include "ui/gfx/geometry/rect_base_impl.h" +#include "ui/gfx/geometry/safe_integer_conversions.h" namespace gfx { diff --git a/chromium/ui/gfx/geometry/rect_f.h b/chromium/ui/gfx/geometry/rect_f.h new file mode 100644 index 00000000000..50ea76bff9f --- /dev/null +++ b/chromium/ui/gfx/geometry/rect_f.h @@ -0,0 +1,113 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_GFX_GEOMETRY_RECT_F_H_ +#define UI_GFX_GEOMETRY_RECT_F_H_ + +#include <string> + +#include "ui/gfx/geometry/point_f.h" +#include "ui/gfx/geometry/rect_base.h" +#include "ui/gfx/geometry/size_f.h" +#include "ui/gfx/geometry/vector2d_f.h" + +namespace gfx { + +class InsetsF; + +// A floating version of gfx::Rect. +class GFX_EXPORT RectF + : public RectBase<RectF, PointF, SizeF, InsetsF, Vector2dF, float> { + public: + RectF() + : RectBase<RectF, PointF, SizeF, InsetsF, Vector2dF, float> + (SizeF()) {} + + RectF(float width, float height) + : RectBase<RectF, PointF, SizeF, InsetsF, Vector2dF, float> + (SizeF(width, height)) {} + + RectF(float x, float y, float width, float height) + : RectBase<RectF, PointF, SizeF, InsetsF, Vector2dF, float> + (PointF(x, y), SizeF(width, height)) {} + + explicit RectF(const SizeF& size) + : RectBase<RectF, PointF, SizeF, InsetsF, Vector2dF, float> + (size) {} + + RectF(const PointF& origin, const SizeF& size) + : RectBase<RectF, PointF, SizeF, InsetsF, Vector2dF, float> + (origin, size) {} + + ~RectF() {} + + // Scales the rectangle by |scale|. + void Scale(float scale) { + Scale(scale, scale); + } + + void Scale(float x_scale, float y_scale) { + set_origin(ScalePoint(origin(), x_scale, y_scale)); + set_size(ScaleSize(size(), x_scale, y_scale)); + } + + // This method reports if the RectF can be safely converted to an integer + // Rect. When it is false, some dimension of the RectF is outside the bounds + // of what an integer can represent, and converting it to a Rect will require + // clamping. + bool IsExpressibleAsRect() const; + + std::string ToString() const; +}; + +inline bool operator==(const RectF& lhs, const RectF& rhs) { + return lhs.origin() == rhs.origin() && lhs.size() == rhs.size(); +} + +inline bool operator!=(const RectF& lhs, const RectF& rhs) { + return !(lhs == rhs); +} + +inline RectF operator+(const RectF& lhs, const Vector2dF& rhs) { + return RectF(lhs.x() + rhs.x(), lhs.y() + rhs.y(), + lhs.width(), lhs.height()); +} + +inline RectF operator-(const RectF& lhs, const Vector2dF& rhs) { + return RectF(lhs.x() - rhs.x(), lhs.y() - rhs.y(), + lhs.width(), lhs.height()); +} + +inline RectF operator+(const Vector2dF& lhs, const RectF& rhs) { + return rhs + lhs; +} + +GFX_EXPORT RectF IntersectRects(const RectF& a, const RectF& b); +GFX_EXPORT RectF UnionRects(const RectF& a, const RectF& b); +GFX_EXPORT RectF SubtractRects(const RectF& a, const RectF& b); + +inline RectF ScaleRect(const RectF& r, float x_scale, float y_scale) { + return RectF(r.x() * x_scale, r.y() * y_scale, + r.width() * x_scale, r.height() * y_scale); +} + +inline RectF ScaleRect(const RectF& r, float scale) { + return ScaleRect(r, scale, scale); +} + +// Constructs a rectangle with |p1| and |p2| as opposite corners. +// +// This could also be thought of as "the smallest rect that contains both +// points", except that we consider points on the right/bottom edges of the +// rect to be outside the rect. So technically one or both points will not be +// contained within the rect, because they will appear on one of these edges. +GFX_EXPORT RectF BoundingRect(const PointF& p1, const PointF& p2); + +#if !defined(COMPILER_MSVC) +extern template class RectBase<RectF, PointF, SizeF, InsetsF, Vector2dF, float>; +#endif + +} // namespace gfx + +#endif // UI_GFX_GEOMETRY_RECT_F_H_ diff --git a/chromium/ui/gfx/rect_unittest.cc b/chromium/ui/gfx/geometry/rect_unittest.cc index 31a156b7910..5fcd76b706b 100644 --- a/chromium/ui/gfx/rect_unittest.cc +++ b/chromium/ui/gfx/geometry/rect_unittest.cc @@ -1,14 +1,17 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <limits> + #include "base/basictypes.h" #include "testing/gtest/include/gtest/gtest.h" -#include "ui/gfx/rect.h" -#include "ui/gfx/rect_conversions.h" -#include "ui/gfx/skia_util.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/rect_conversions.h" -#include <limits> +#if defined(OS_WIN) +#include <windows.h> +#endif namespace gfx { @@ -414,20 +417,6 @@ TEST(RectTest, SharesEdgeWith) { EXPECT_FALSE(r.SharesEdgeWith(just_right_no_edge)); } -TEST(RectTest, SkiaRectConversions) { - Rect isrc(10, 20, 30, 40); - RectF fsrc(10.5f, 20.5f, 30.5f, 40.5f); - - SkIRect skirect = RectToSkIRect(isrc); - EXPECT_EQ(isrc.ToString(), SkIRectToRect(skirect).ToString()); - - SkRect skrect = RectToSkRect(isrc); - EXPECT_EQ(gfx::RectF(isrc).ToString(), SkRectToRectF(skrect).ToString()); - - skrect = RectFToSkRect(fsrc); - EXPECT_EQ(fsrc.ToString(), SkRectToRectF(skrect).ToString()); -} - // Similar to EXPECT_FLOAT_EQ, but lets NaN equal NaN #define EXPECT_FLOAT_AND_NAN_EQ(a, b) \ { if (a == a || b == b) { EXPECT_FLOAT_EQ(a, b); } } diff --git a/chromium/ui/gfx/geometry/safe_integer_conversions.h b/chromium/ui/gfx/geometry/safe_integer_conversions.h new file mode 100644 index 00000000000..4d289ecabaf --- /dev/null +++ b/chromium/ui/gfx/geometry/safe_integer_conversions.h @@ -0,0 +1,54 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_GFX_GEOMETRY_SAFE_INTEGER_CONVERSIONS_H_ +#define UI_GFX_GEOMETRY_SAFE_INTEGER_CONVERSIONS_H_ + +#include <cmath> +#include <limits> + +#include "ui/gfx/gfx_export.h" + +namespace gfx { + +inline int ClampToInt(float value) { + if (value != value) + return 0; // no int NaN. + if (value >= std::numeric_limits<int>::max()) + return std::numeric_limits<int>::max(); + if (value <= std::numeric_limits<int>::min()) + return std::numeric_limits<int>::min(); + return static_cast<int>(value); +} + +inline int ToFlooredInt(float value) { + return ClampToInt(std::floor(value)); +} + +inline int ToCeiledInt(float value) { + return ClampToInt(std::ceil(value)); +} + +inline int ToRoundedInt(float value) { + float rounded; + if (value >= 0.0f) + rounded = std::floor(value + 0.5f); + else + rounded = std::ceil(value - 0.5f); + return ClampToInt(rounded); +} + +inline bool IsExpressibleAsInt(float value) { + if (value != value) + return false; // no int NaN. + if (value > std::numeric_limits<int>::max()) + return false; + if (value < std::numeric_limits<int>::min()) + return false; + return true; +} + +} // namespace gfx + +#endif // UI_GFX_GEOMETRY_SAFE_INTEGER_CONVERSIONS_H_ diff --git a/chromium/ui/gfx/safe_integer_conversions_unittest.cc b/chromium/ui/gfx/geometry/safe_integer_conversions_unittest.cc index 1268f8bbf32..e00671b264a 100644 --- a/chromium/ui/gfx/safe_integer_conversions_unittest.cc +++ b/chromium/ui/gfx/geometry/safe_integer_conversions_unittest.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ui/gfx/safe_integer_conversions.h" +#include "ui/gfx/geometry/safe_integer_conversions.h" #include <limits> diff --git a/chromium/ui/gfx/size.cc b/chromium/ui/gfx/geometry/size.cc index aa003e8c6df..38ec4b3a516 100644 --- a/chromium/ui/gfx/size.cc +++ b/chromium/ui/gfx/geometry/size.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ui/gfx/size.h" +#include "ui/gfx/geometry/size.h" #if defined(OS_WIN) #include <windows.h> diff --git a/chromium/ui/gfx/geometry/size.h b/chromium/ui/gfx/geometry/size.h new file mode 100644 index 00000000000..78984b073f4 --- /dev/null +++ b/chromium/ui/gfx/geometry/size.h @@ -0,0 +1,67 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_GFX_GEOMETRY_SIZE_H_ +#define UI_GFX_GEOMETRY_SIZE_H_ + +#include <string> + +#include "base/compiler_specific.h" +#include "ui/gfx/geometry/size_base.h" +#include "ui/gfx/geometry/size_f.h" +#include "ui/gfx/gfx_export.h" + +#if defined(OS_WIN) +typedef struct tagSIZE SIZE; +#elif defined(OS_IOS) +#include <CoreGraphics/CoreGraphics.h> +#elif defined(OS_MACOSX) +#include <ApplicationServices/ApplicationServices.h> +#endif + +namespace gfx { + +// A size has width and height values. +class GFX_EXPORT Size : public SizeBase<Size, int> { + public: + Size() : SizeBase<Size, int>(0, 0) {} + Size(int width, int height) : SizeBase<Size, int>(width, height) {} +#if defined(OS_MACOSX) + explicit Size(const CGSize& s); +#endif + + ~Size() {} + +#if defined(OS_MACOSX) + Size& operator=(const CGSize& s); +#endif + +#if defined(OS_WIN) + SIZE ToSIZE() const; +#elif defined(OS_MACOSX) + CGSize ToCGSize() const; +#endif + + operator SizeF() const { + return SizeF(width(), height()); + } + + std::string ToString() const; +}; + +inline bool operator==(const Size& lhs, const Size& rhs) { + return lhs.width() == rhs.width() && lhs.height() == rhs.height(); +} + +inline bool operator!=(const Size& lhs, const Size& rhs) { + return !(lhs == rhs); +} + +#if !defined(COMPILER_MSVC) +extern template class SizeBase<Size, int>; +#endif + +} // namespace gfx + +#endif // UI_GFX_GEOMETRY_SIZE_H_ diff --git a/chromium/ui/gfx/size_base.h b/chromium/ui/gfx/geometry/size_base.h index c8349dc4b77..88a98ef4584 100644 --- a/chromium/ui/gfx/size_base.h +++ b/chromium/ui/gfx/geometry/size_base.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_GFX_SIZE_BASE_H_ -#define UI_GFX_SIZE_BASE_H_ +#ifndef UI_GFX_GEOMETRY_SIZE_BASE_H_ +#define UI_GFX_GEOMETRY_SIZE_BASE_H_ #include "ui/gfx/gfx_export.h" @@ -66,4 +66,4 @@ class GFX_EXPORT SizeBase { } // namespace gfx -#endif // UI_GFX_SIZE_BASE_H_ +#endif // UI_GFX_GEOMETRY_SIZE_BASE_H_ diff --git a/chromium/ui/gfx/size_conversions.cc b/chromium/ui/gfx/geometry/size_conversions.cc index eacbeb4fc8d..c924e86c9ae 100644 --- a/chromium/ui/gfx/size_conversions.cc +++ b/chromium/ui/gfx/geometry/size_conversions.cc @@ -2,9 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ui/gfx/size_conversions.h" +#include "ui/gfx/geometry/size_conversions.h" -#include "ui/gfx/safe_integer_conversions.h" +#include "ui/gfx/geometry/safe_integer_conversions.h" namespace gfx { diff --git a/chromium/ui/gfx/geometry/size_conversions.h b/chromium/ui/gfx/geometry/size_conversions.h new file mode 100644 index 00000000000..96fb79f93c2 --- /dev/null +++ b/chromium/ui/gfx/geometry/size_conversions.h @@ -0,0 +1,24 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_GFX_GEOMETRY_SIZE_CONVERSIONS_H_ +#define UI_GFX_GEOMETRY_SIZE_CONVERSIONS_H_ + +#include "ui/gfx/geometry/size.h" +#include "ui/gfx/geometry/size_f.h" + +namespace gfx { + +// Returns a Size with each component from the input SizeF floored. +GFX_EXPORT Size ToFlooredSize(const SizeF& size); + +// Returns a Size with each component from the input SizeF ceiled. +GFX_EXPORT Size ToCeiledSize(const SizeF& size); + +// Returns a Size with each component from the input SizeF rounded. +GFX_EXPORT Size ToRoundedSize(const SizeF& size); + +} // namespace gfx + +#endif // UI_GFX_GEOMETRY_SIZE_CONVERSIONS_H_ diff --git a/chromium/ui/gfx/size_f.cc b/chromium/ui/gfx/geometry/size_f.cc index 6eba8849b22..10c144575e7 100644 --- a/chromium/ui/gfx/size_f.cc +++ b/chromium/ui/gfx/geometry/size_f.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ui/gfx/size_f.h" +#include "ui/gfx/geometry/size_f.h" #include "base/strings/stringprintf.h" diff --git a/chromium/ui/gfx/geometry/size_f.h b/chromium/ui/gfx/geometry/size_f.h new file mode 100644 index 00000000000..8ec5628e62d --- /dev/null +++ b/chromium/ui/gfx/geometry/size_f.h @@ -0,0 +1,54 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_GFX_GEOMETRY_SIZE_F_H_ +#define UI_GFX_GEOMETRY_SIZE_F_H_ + +#include <string> + +#include "base/compiler_specific.h" +#include "ui/gfx/geometry/size_base.h" +#include "ui/gfx/gfx_export.h" + +namespace gfx { + +// A floating version of gfx::Size. +class GFX_EXPORT SizeF : public SizeBase<SizeF, float> { + public: + SizeF() : SizeBase<SizeF, float>(0, 0) {} + SizeF(float width, float height) : SizeBase<SizeF, float>(width, height) {} + ~SizeF() {} + + void Scale(float scale) { + Scale(scale, scale); + } + + void Scale(float x_scale, float y_scale) { + SetSize(width() * x_scale, height() * y_scale); + } + + std::string ToString() const; +}; + +inline bool operator==(const SizeF& lhs, const SizeF& rhs) { + return lhs.width() == rhs.width() && lhs.height() == rhs.height(); +} + +inline bool operator!=(const SizeF& lhs, const SizeF& rhs) { + return !(lhs == rhs); +} + +GFX_EXPORT SizeF ScaleSize(const SizeF& p, float x_scale, float y_scale); + +inline SizeF ScaleSize(const SizeF& p, float scale) { + return ScaleSize(p, scale, scale); +} + +#if !defined(COMPILER_MSVC) +extern template class SizeBase<SizeF, float>; +#endif + +} // namespace gfx + +#endif // UI_GFX_GEOMETRY_SIZE_F_H_ diff --git a/chromium/ui/gfx/size_unittest.cc b/chromium/ui/gfx/geometry/size_unittest.cc index 9f109b3f04b..15892833307 100644 --- a/chromium/ui/gfx/size_unittest.cc +++ b/chromium/ui/gfx/geometry/size_unittest.cc @@ -2,12 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ui/gfx/size_base.h" - #include "testing/gtest/include/gtest/gtest.h" -#include "ui/gfx/size.h" -#include "ui/gfx/size_conversions.h" -#include "ui/gfx/size_f.h" +#include "ui/gfx/geometry/size.h" +#include "ui/gfx/geometry/size_base.h" +#include "ui/gfx/geometry/size_conversions.h" +#include "ui/gfx/geometry/size_f.h" namespace gfx { diff --git a/chromium/ui/gfx/vector2d.cc b/chromium/ui/gfx/geometry/vector2d.cc index 9e685e45d1d..d9f57fd3b10 100644 --- a/chromium/ui/gfx/vector2d.cc +++ b/chromium/ui/gfx/geometry/vector2d.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ui/gfx/vector2d.h" +#include "ui/gfx/geometry/vector2d.h" #include <cmath> diff --git a/chromium/ui/gfx/geometry/vector2d.h b/chromium/ui/gfx/geometry/vector2d.h new file mode 100644 index 00000000000..12f29a956d7 --- /dev/null +++ b/chromium/ui/gfx/geometry/vector2d.h @@ -0,0 +1,91 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Defines a simple integer vector class. This class is used to indicate a +// distance in two dimensions between two points. Subtracting two points should +// produce a vector, and adding a vector to a point produces the point at the +// vector's distance from the original point. + +#ifndef UI_GFX_GEOMETRY_VECTOR2D_H_ +#define UI_GFX_GEOMETRY_VECTOR2D_H_ + +#include <string> + +#include "base/basictypes.h" +#include "ui/gfx/geometry/vector2d_f.h" +#include "ui/gfx/gfx_export.h" + +namespace gfx { + +class GFX_EXPORT Vector2d { + public: + Vector2d() : x_(0), y_(0) {} + Vector2d(int x, int y) : x_(x), y_(y) {} + + int x() const { return x_; } + void set_x(int x) { x_ = x; } + + int y() const { return y_; } + void set_y(int y) { y_ = y; } + + // True if both components of the vector are 0. + bool IsZero() const; + + // Add the components of the |other| vector to the current vector. + void Add(const Vector2d& other); + // Subtract the components of the |other| vector from the current vector. + void Subtract(const Vector2d& other); + + void operator+=(const Vector2d& other) { Add(other); } + void operator-=(const Vector2d& other) { Subtract(other); } + + void SetToMin(const Vector2d& other) { + x_ = x_ <= other.x_ ? x_ : other.x_; + y_ = y_ <= other.y_ ? y_ : other.y_; + } + + void SetToMax(const Vector2d& other) { + x_ = x_ >= other.x_ ? x_ : other.x_; + y_ = y_ >= other.y_ ? y_ : other.y_; + } + + // Gives the square of the diagonal length of the vector. Since this is + // cheaper to compute than Length(), it is useful when you want to compare + // relative lengths of different vectors without needing the actual lengths. + int64 LengthSquared() const; + // Gives the diagonal length of the vector. + float Length() const; + + std::string ToString() const; + + operator Vector2dF() const { return Vector2dF(x_, y_); } + + private: + int x_; + int y_; +}; + +inline bool operator==(const Vector2d& lhs, const Vector2d& rhs) { + return lhs.x() == rhs.x() && lhs.y() == rhs.y(); +} + +inline Vector2d operator-(const Vector2d& v) { + return Vector2d(-v.x(), -v.y()); +} + +inline Vector2d operator+(const Vector2d& lhs, const Vector2d& rhs) { + Vector2d result = lhs; + result.Add(rhs); + return result; +} + +inline Vector2d operator-(const Vector2d& lhs, const Vector2d& rhs) { + Vector2d result = lhs; + result.Add(-rhs); + return result; +} + +} // namespace gfx + +#endif // UI_GFX_GEOMETRY_VECTOR2D_H_ diff --git a/chromium/ui/gfx/vector2d_conversions.cc b/chromium/ui/gfx/geometry/vector2d_conversions.cc index 457e9f708a2..dceb69e2ffe 100644 --- a/chromium/ui/gfx/vector2d_conversions.cc +++ b/chromium/ui/gfx/geometry/vector2d_conversions.cc @@ -2,9 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ui/gfx/vector2d_conversions.h" +#include "ui/gfx/geometry/vector2d_conversions.h" -#include "ui/gfx/safe_integer_conversions.h" +#include "ui/gfx/geometry/safe_integer_conversions.h" namespace gfx { diff --git a/chromium/ui/gfx/geometry/vector2d_conversions.h b/chromium/ui/gfx/geometry/vector2d_conversions.h new file mode 100644 index 00000000000..f4e16ae4be7 --- /dev/null +++ b/chromium/ui/gfx/geometry/vector2d_conversions.h @@ -0,0 +1,24 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_GFX_GEOMETRY_VECTOR2D_CONVERSIONS_H_ +#define UI_GFX_GEOMETRY_VECTOR2D_CONVERSIONS_H_ + +#include "ui/gfx/geometry/vector2d.h" +#include "ui/gfx/geometry/vector2d_f.h" + +namespace gfx { + +// Returns a Vector2d with each component from the input Vector2dF floored. +GFX_EXPORT Vector2d ToFlooredVector2d(const Vector2dF& vector2d); + +// Returns a Vector2d with each component from the input Vector2dF ceiled. +GFX_EXPORT Vector2d ToCeiledVector2d(const Vector2dF& vector2d); + +// Returns a Vector2d with each component from the input Vector2dF rounded. +GFX_EXPORT Vector2d ToRoundedVector2d(const Vector2dF& vector2d); + +} // namespace gfx + +#endif // UI_GFX_GEOMETRY_VECTOR2D_CONVERSIONS_H_ diff --git a/chromium/ui/gfx/vector2d_f.cc b/chromium/ui/gfx/geometry/vector2d_f.cc index 2ad267074b3..ccb15ae0c4c 100644 --- a/chromium/ui/gfx/vector2d_f.cc +++ b/chromium/ui/gfx/geometry/vector2d_f.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ui/gfx/vector2d_f.h" +#include "ui/gfx/geometry/vector2d_f.h" #include <cmath> diff --git a/chromium/ui/gfx/geometry/vector2d_f.h b/chromium/ui/gfx/geometry/vector2d_f.h new file mode 100644 index 00000000000..4bab99e5825 --- /dev/null +++ b/chromium/ui/gfx/geometry/vector2d_f.h @@ -0,0 +1,112 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Defines a simple float vector class. This class is used to indicate a +// distance in two dimensions between two points. Subtracting two points should +// produce a vector, and adding a vector to a point produces the point at the +// vector's distance from the original point. + +#ifndef UI_GFX_GEOMETRY_VECTOR2D_F_H_ +#define UI_GFX_GEOMETRY_VECTOR2D_F_H_ + +#include <string> + +#include "ui/gfx/gfx_export.h" + +namespace gfx { + +class GFX_EXPORT Vector2dF { + public: + Vector2dF() : x_(0), y_(0) {} + Vector2dF(float x, float y) : x_(x), y_(y) {} + + float x() const { return x_; } + void set_x(float x) { x_ = x; } + + float y() const { return y_; } + void set_y(float y) { y_ = y; } + + // True if both components of the vector are 0. + bool IsZero() const; + + // Add the components of the |other| vector to the current vector. + void Add(const Vector2dF& other); + // Subtract the components of the |other| vector from the current vector. + void Subtract(const Vector2dF& other); + + void operator+=(const Vector2dF& other) { Add(other); } + void operator-=(const Vector2dF& other) { Subtract(other); } + + void SetToMin(const Vector2dF& other) { + x_ = x_ <= other.x_ ? x_ : other.x_; + y_ = y_ <= other.y_ ? y_ : other.y_; + } + + void SetToMax(const Vector2dF& other) { + x_ = x_ >= other.x_ ? x_ : other.x_; + y_ = y_ >= other.y_ ? y_ : other.y_; + } + + // Gives the square of the diagonal length of the vector. + double LengthSquared() const; + // Gives the diagonal length of the vector. + float Length() const; + + // Scale the x and y components of the vector by |scale|. + void Scale(float scale) { Scale(scale, scale); } + // Scale the x and y components of the vector by |x_scale| and |y_scale| + // respectively. + void Scale(float x_scale, float y_scale); + + std::string ToString() const; + + private: + float x_; + float y_; +}; + +inline bool operator==(const Vector2dF& lhs, const Vector2dF& rhs) { + return lhs.x() == rhs.x() && lhs.y() == rhs.y(); +} + +inline bool operator!=(const Vector2dF& lhs, const Vector2dF& rhs) { + return !(lhs == rhs); +} + +inline Vector2dF operator-(const Vector2dF& v) { + return Vector2dF(-v.x(), -v.y()); +} + +inline Vector2dF operator+(const Vector2dF& lhs, const Vector2dF& rhs) { + Vector2dF result = lhs; + result.Add(rhs); + return result; +} + +inline Vector2dF operator-(const Vector2dF& lhs, const Vector2dF& rhs) { + Vector2dF result = lhs; + result.Add(-rhs); + return result; +} + +// Return the cross product of two vectors. +GFX_EXPORT double CrossProduct(const Vector2dF& lhs, const Vector2dF& rhs); + +// Return the dot product of two vectors. +GFX_EXPORT double DotProduct(const Vector2dF& lhs, const Vector2dF& rhs); + +// Return a vector that is |v| scaled by the given scale factors along each +// axis. +GFX_EXPORT Vector2dF ScaleVector2d(const Vector2dF& v, + float x_scale, + float y_scale); + +// Return a vector that is |v| scaled by the given scale factor. +inline Vector2dF ScaleVector2d(const Vector2dF& v, float scale) { + return ScaleVector2d(v, scale, scale); +} + +} // namespace gfx + +#endif // UI_GFX_GEOMETRY_VECTOR2D_F_H_ diff --git a/chromium/ui/gfx/vector2d_unittest.cc b/chromium/ui/gfx/geometry/vector2d_unittest.cc index 5d9e21ea66d..38af69405f5 100644 --- a/chromium/ui/gfx/vector2d_unittest.cc +++ b/chromium/ui/gfx/geometry/vector2d_unittest.cc @@ -7,8 +7,8 @@ #include "base/basictypes.h" #include "testing/gtest/include/gtest/gtest.h" -#include "ui/gfx/vector2d.h" -#include "ui/gfx/vector2d_f.h" +#include "ui/gfx/geometry/vector2d.h" +#include "ui/gfx/geometry/vector2d_f.h" namespace gfx { diff --git a/chromium/ui/gfx/vector3d_f.cc b/chromium/ui/gfx/geometry/vector3d_f.cc index 233e0542673..a30f9db5cbe 100644 --- a/chromium/ui/gfx/vector3d_f.cc +++ b/chromium/ui/gfx/geometry/vector3d_f.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ui/gfx/vector3d_f.h" +#include "ui/gfx/geometry/vector3d_f.h" #include <cmath> diff --git a/chromium/ui/gfx/geometry/vector3d_f.h b/chromium/ui/gfx/geometry/vector3d_f.h new file mode 100644 index 00000000000..8f8b75d91e7 --- /dev/null +++ b/chromium/ui/gfx/geometry/vector3d_f.h @@ -0,0 +1,124 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Defines a simple float vector class. This class is used to indicate a +// distance in two dimensions between two points. Subtracting two points should +// produce a vector, and adding a vector to a point produces the point at the +// vector's distance from the original point. + +#ifndef UI_GFX_GEOMETRY_VECTOR3D_F_H_ +#define UI_GFX_GEOMETRY_VECTOR3D_F_H_ + +#include <string> + +#include "ui/gfx/geometry/vector2d_f.h" +#include "ui/gfx/gfx_export.h" + +namespace gfx { + +class GFX_EXPORT Vector3dF { + public: + Vector3dF(); + Vector3dF(float x, float y, float z); + + explicit Vector3dF(const Vector2dF& other); + + float x() const { return x_; } + void set_x(float x) { x_ = x; } + + float y() const { return y_; } + void set_y(float y) { y_ = y; } + + float z() const { return z_; } + void set_z(float z) { z_ = z; } + + // True if all components of the vector are 0. + bool IsZero() const; + + // Add the components of the |other| vector to the current vector. + void Add(const Vector3dF& other); + // Subtract the components of the |other| vector from the current vector. + void Subtract(const Vector3dF& other); + + void operator+=(const Vector3dF& other) { Add(other); } + void operator-=(const Vector3dF& other) { Subtract(other); } + + void SetToMin(const Vector3dF& other) { + x_ = x_ <= other.x_ ? x_ : other.x_; + y_ = y_ <= other.y_ ? y_ : other.y_; + z_ = z_ <= other.z_ ? z_ : other.z_; + } + + void SetToMax(const Vector3dF& other) { + x_ = x_ >= other.x_ ? x_ : other.x_; + y_ = y_ >= other.y_ ? y_ : other.y_; + z_ = z_ >= other.z_ ? z_ : other.z_; + } + + // Gives the square of the diagonal length of the vector. + double LengthSquared() const; + // Gives the diagonal length of the vector. + float Length() const; + + // Scale all components of the vector by |scale|. + void Scale(float scale) { Scale(scale, scale, scale); } + // Scale the each component of the vector by the given scale factors. + void Scale(float x_scale, float y_scale, float z_scale); + + // Take the cross product of this vector with |other| and become the result. + void Cross(const Vector3dF& other); + + std::string ToString() const; + + private: + float x_; + float y_; + float z_; +}; + +inline bool operator==(const Vector3dF& lhs, const Vector3dF& rhs) { + return lhs.x() == rhs.x() && lhs.y() == rhs.y() && lhs.z() == rhs.z(); +} + +inline Vector3dF operator-(const Vector3dF& v) { + return Vector3dF(-v.x(), -v.y(), -v.z()); +} + +inline Vector3dF operator+(const Vector3dF& lhs, const Vector3dF& rhs) { + Vector3dF result = lhs; + result.Add(rhs); + return result; +} + +inline Vector3dF operator-(const Vector3dF& lhs, const Vector3dF& rhs) { + Vector3dF result = lhs; + result.Add(-rhs); + return result; +} + +// Return the cross product of two vectors. +inline Vector3dF CrossProduct(const Vector3dF& lhs, const Vector3dF& rhs) { + Vector3dF result = lhs; + result.Cross(rhs); + return result; +} + +// Return the dot product of two vectors. +GFX_EXPORT float DotProduct(const Vector3dF& lhs, const Vector3dF& rhs); + +// Return a vector that is |v| scaled by the given scale factors along each +// axis. +GFX_EXPORT Vector3dF ScaleVector3d(const Vector3dF& v, + float x_scale, + float y_scale, + float z_scale); + +// Return a vector that is |v| scaled by the given scale factor. +inline Vector3dF ScaleVector3d(const Vector3dF& v, float scale) { + return ScaleVector3d(v, scale, scale, scale); +} + +} // namespace gfx + +#endif // UI_GFX_GEOMETRY_VECTOR3D_F_H_ diff --git a/chromium/ui/gfx/vector3d_unittest.cc b/chromium/ui/gfx/geometry/vector3d_unittest.cc index 5b3bd7e91bf..d5ec8d6700c 100644 --- a/chromium/ui/gfx/vector3d_unittest.cc +++ b/chromium/ui/gfx/geometry/vector3d_unittest.cc @@ -7,7 +7,7 @@ #include "base/basictypes.h" #include "testing/gtest/include/gtest/gtest.h" -#include "ui/gfx/vector3d_f.h" +#include "ui/gfx/geometry/vector3d_f.h" namespace gfx { diff --git a/chromium/ui/gfx/gfx.gyp b/chromium/ui/gfx/gfx.gyp index 5fb17d52ee6..3077ac90c18 100644 --- a/chromium/ui/gfx/gfx.gyp +++ b/chromium/ui/gfx/gfx.gyp @@ -8,6 +8,67 @@ }, 'targets': [ { + 'target_name': 'gfx_geometry', + 'type': '<(component)', + 'dependencies': [ + '<(DEPTH)/base/base.gyp:base', + ], + 'defines': [ + 'GFX_IMPLEMENTATION', + ], + 'sources': [ + 'geometry/box_f.cc', + 'geometry/box_f.h', + 'geometry/cubic_bezier.h', + 'geometry/cubic_bezier.cc', + 'geometry/insets.cc', + 'geometry/insets.h', + 'geometry/insets_base.h', + 'geometry/insets_f.cc', + 'geometry/insets_f.h', + 'geometry/matrix3_f.cc', + 'geometry/matrix3_f.h', + 'geometry/point.cc', + 'geometry/point.h', + 'geometry/point3_f.cc', + 'geometry/point3_f.h', + 'geometry/point_base.h', + 'geometry/point_conversions.cc', + 'geometry/point_conversions.h', + 'geometry/point_f.cc', + 'geometry/point_f.h', + 'geometry/quad_f.cc', + 'geometry/quad_f.h', + 'geometry/rect.cc', + 'geometry/rect.h', + 'geometry/rect_base.h', + 'geometry/rect_base_impl.h', + 'geometry/rect_conversions.cc', + 'geometry/rect_conversions.h', + 'geometry/rect_f.cc', + 'geometry/rect_f.h', + 'geometry/r_tree.h', + 'geometry/r_tree_base.cc', + 'geometry/r_tree_base.h', + 'geometry/safe_integer_conversions.h', + 'geometry/size.cc', + 'geometry/size.h', + 'geometry/size_base.h', + 'geometry/size_conversions.cc', + 'geometry/size_conversions.h', + 'geometry/size_f.cc', + 'geometry/size_f.h', + 'geometry/vector2d.cc', + 'geometry/vector2d.h', + 'geometry/vector2d_conversions.cc', + 'geometry/vector2d_conversions.h', + 'geometry/vector2d_f.cc', + 'geometry/vector2d_f.h', + 'geometry/vector3d_f.cc', + 'geometry/vector3d_f.h', + ], + }, + { 'target_name': 'gfx', 'type': '<(component)', 'dependencies': [ @@ -15,13 +76,13 @@ '<(DEPTH)/base/base.gyp:base_i18n', '<(DEPTH)/base/base.gyp:base_static', '<(DEPTH)/base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations', - '<(DEPTH)/net/net.gyp:net', '<(DEPTH)/skia/skia.gyp:skia', + '<(DEPTH)/third_party/harfbuzz-ng/harfbuzz.gyp:harfbuzz-ng', '<(DEPTH)/third_party/icu/icu.gyp:icui18n', '<(DEPTH)/third_party/icu/icu.gyp:icuuc', '<(DEPTH)/third_party/libpng/libpng.gyp:libpng', '<(DEPTH)/third_party/zlib/zlib.gyp:zlib', - '<(DEPTH)/url/url.gyp:url_lib', + 'gfx_geometry', ], # text_elider.h includes ICU headers. 'export_dependent_settings': [ @@ -32,6 +93,9 @@ 'defines': [ 'GFX_IMPLEMENTATION', ], + 'include_dirs': [ + '<(DEPTH)/third_party/icu/source/common' + ], 'sources': [ 'android/device_display_info.cc', 'android/device_display_info.h', @@ -39,6 +103,8 @@ 'android/gfx_jni_registrar.h', 'android/java_bitmap.cc', 'android/java_bitmap.h', + 'android/scroller.cc', + 'android/scroller.h', 'android/shared_device_display_info.cc', 'android/shared_device_display_info.h', 'android/view_configuration.cc', @@ -62,14 +128,10 @@ 'animation/tween.h', 'blit.cc', 'blit.h', - 'box_f.cc', - 'box_f.h', 'break_list.h', 'canvas.cc', 'canvas.h', 'canvas_android.cc', - 'canvas_paint_gtk.cc', - 'canvas_paint_gtk.h', 'canvas_paint_mac.h', 'canvas_paint_mac.mm', 'canvas_paint_win.cc', @@ -84,7 +146,7 @@ 'color_analysis.h', 'color_profile.cc', 'color_profile.h', - 'color_profile_mac.cc', + 'color_profile_mac.mm', 'color_profile_win.cc', 'color_utils.cc', 'color_utils.h', @@ -94,18 +156,20 @@ 'display_observer.h', 'favicon_size.cc', 'favicon_size.h', - 'frame_time.h', 'font.cc', 'font.h', 'font_fallback_win.cc', 'font_fallback_win.h', 'font_list.cc', 'font_list.h', + 'font_list_impl.cc', + 'font_list_impl.h', 'font_render_params_android.cc', 'font_render_params_linux.cc', 'font_render_params_linux.h', 'font_smoothing_win.cc', 'font_smoothing_win.h', + 'frame_time.h', 'gfx_export.h', 'gfx_paths.cc', 'gfx_paths.h', @@ -135,39 +199,20 @@ 'image/image_util.cc', 'image/image_util.h', 'image/image_util_ios.mm', - 'insets.cc', - 'insets.h', - 'insets_base.h', - 'insets_f.cc', - 'insets_f.h', 'interpolated_transform.cc', 'interpolated_transform.h', + 'linux_font_delegate.cc', + 'linux_font_delegate.h', 'mac/scoped_ns_disable_screen_updates.h', - 'matrix3_f.cc', - 'matrix3_f.h', 'native_widget_types.h', - 'ozone/dri/dri_skbitmap.cc', - 'ozone/dri/dri_skbitmap.h', - 'ozone/dri/dri_surface.cc', - 'ozone/dri/dri_surface.h', - 'ozone/dri/dri_surface_factory.cc', - 'ozone/dri/dri_surface_factory.h', - 'ozone/dri/dri_vsync_provider.cc', - 'ozone/dri/dri_vsync_provider.h', - 'ozone/dri/dri_wrapper.cc', - 'ozone/dri/dri_wrapper.h', - 'ozone/dri/hardware_display_controller.cc', - 'ozone/dri/hardware_display_controller.h', - 'ozone/impl/file_surface_factory.cc', - 'ozone/impl/file_surface_factory.h', - 'ozone/surface_factory_ozone.cc', - 'ozone/surface_factory_ozone.h', + 'nine_image_painter.cc', + 'nine_image_painter.h', + 'overlay_transform.h', 'pango_util.cc', 'pango_util.h', 'path.cc', 'path.h', 'path_aura.cc', - 'path_gtk.cc', 'path_win.cc', 'path_win.h', 'path_x11.cc', @@ -183,31 +228,14 @@ 'platform_font_pango.h', 'platform_font_win.cc', 'platform_font_win.h', - 'point.cc', - 'point.h', - 'point3_f.cc', - 'point3_f.h', - 'point_base.h', - 'point_conversions.cc', - 'point_conversions.h', - 'point_f.cc', - 'point_f.h', - 'quad_f.cc', - 'quad_f.h', 'range/range.cc', 'range/range.h', 'range/range_mac.mm', 'range/range_win.cc', - 'rect.cc', - 'rect.h', - 'rect_base.h', - 'rect_base_impl.h', - 'rect_conversions.cc', - 'rect_conversions.h', - 'rect_f.cc', - 'rect_f.h', 'render_text.cc', 'render_text.h', + 'render_text_harfbuzz.cc', + 'render_text_harfbuzz.h', 'render_text_mac.cc', 'render_text_mac.h', 'render_text_ozone.cc', @@ -215,7 +243,6 @@ 'render_text_pango.h', 'render_text_win.cc', 'render_text_win.h', - 'safe_integer_conversions.h', 'scoped_canvas.h', 'scoped_cg_context_save_gstate_mac.h', 'scoped_ns_graphics_context_save_gstate_mac.h', @@ -226,7 +253,6 @@ 'screen.h', 'screen_android.cc', 'screen_aura.cc', - 'screen_gtk.cc', 'screen_ios.mm', 'screen_mac.mm', 'screen_win.cc', @@ -239,19 +265,10 @@ 'sequential_id_generator.h', 'shadow_value.cc', 'shadow_value.h', - 'size.cc', - 'size.h', - 'size_base.h', - 'size_conversions.cc', - 'size_conversions.h', - 'size_f.cc', - 'size_f.h', 'skbitmap_operations.cc', 'skbitmap_operations.h', 'skia_util.cc', 'skia_util.h', - 'skia_utils_gtk.cc', - 'skia_utils_gtk.h', 'switches.cc', 'switches.h', 'sys_color_change_listener.cc', @@ -271,14 +288,6 @@ 'ui_gfx_exports.cc', 'utf16_indexing.cc', 'utf16_indexing.h', - 'vector2d.cc', - 'vector2d.h', - 'vector2d_conversions.cc', - 'vector2d_conversions.h', - 'vector2d_f.cc', - 'vector2d_f.h', - 'vector3d_f.cc', - 'vector3d_f.h', 'vsync_provider.h', 'win/dpi.cc', 'win/dpi.h', @@ -289,10 +298,6 @@ 'win/singleton_hwnd.h', 'win/window_impl.cc', 'win/window_impl.h', - 'x/x11_atom_cache.cc', - 'x/x11_atom_cache.h', - 'x/x11_types.cc', - 'x/x11_types.h', ], 'conditions': [ ['OS=="ios"', { @@ -316,24 +321,6 @@ 'canvas_skia.cc', ], }], - ['toolkit_uses_gtk == 1', { - 'dependencies': [ - '<(DEPTH)/build/linux/system.gyp:gtk', - ], - 'sources': [ - 'gtk_native_view_id_manager.cc', - 'gtk_native_view_id_manager.h', - 'gtk_preserve_window.cc', - 'gtk_preserve_window.h', - 'gdk_compat.h', - 'gtk_compat.h', - 'gtk_util.cc', - 'gtk_util.h', - 'image/cairo_cached_surface.cc', - 'image/cairo_cached_surface.h', - 'scoped_gobject.h', - ], - }], ['OS=="win"', { 'sources': [ 'gdi_util.cc', @@ -350,7 +337,6 @@ 'sources!': [ 'animation/throb_animation.cc', 'display_observer.cc', - 'path.cc', 'selection_model.cc', ], 'dependencies': [ @@ -363,6 +349,22 @@ ], }, }], + ['use_aura==0 and toolkit_views==0', { + 'sources!': [ + 'nine_image_painter.cc', + 'nine_image_painter.h', + ], + }], + ['OS=="android" and use_aura==0', { + 'sources!': [ + 'path.cc', + ], + }], + ['OS=="android" and use_aura==1', { + 'sources!': [ + 'screen_android.cc', + ], + }], ['OS=="android" and android_webview_build==0', { 'dependencies': [ '<(DEPTH)/base/base.gyp:base_java', @@ -372,9 +374,17 @@ 'sources!': [ 'render_text.cc', 'render_text.h', + 'render_text_harfbuzz.cc', + 'render_text_harfbuzz.h', 'text_utils_skia.cc', ], }], + ['use_x11==1', { + 'dependencies': [ + '../../build/linux/system.gyp:x11', + 'x/gfx_x11.gyp:gfx_x11', + ], + }], ['use_pango==1', { 'dependencies': [ '<(DEPTH)/build/linux/system.gyp:pangocairo', @@ -384,9 +394,10 @@ 'render_text_ozone.cc', ], }], - ['ozone_platform_dri==1', { + ['desktop_linux==1 or chromeos==1', { 'dependencies': [ - '<(DEPTH)/build/linux/system.gyp:dridrm', + # font_render_params_linux.cc uses fontconfig + '<(DEPTH)/build/linux/system.gyp:fontconfig', ], }], ], @@ -399,38 +410,58 @@ ], }], ], - } + }, + { + 'target_name': 'gfx_test_support', + 'sources': [ + 'test/gfx_util.cc', + 'test/gfx_util.h', + 'test/ui_cocoa_test_helper.h', + 'test/ui_cocoa_test_helper.mm', + ], + 'dependencies': [ + '../../base/base.gyp:base', + '../../skia/skia.gyp:skia', + '../../testing/gtest.gyp:gtest', + ], + 'conditions': [ + ['OS == "mac"', { + 'link_settings': { + 'libraries': [ + '$(SDKROOT)/System/Library/Frameworks/AppKit.framework', + ], + }, + }], + ['OS!="ios"', { + 'type': 'static_library', + }, { # OS=="ios" + # None of the sources in this target are built on iOS, resulting in + # link errors when building targets that depend on this target + # because the static library isn't found. If this target is changed + # to have sources that are built on iOS, the target should be changed + # to be of type static_library on all platforms. + 'type': 'none', + # The cocoa files don't apply to iOS. + 'sources/': [ + ['exclude', 'cocoa'] + ], + }], + ], + }, ], 'conditions': [ ['OS=="android"' , { 'targets': [ { - 'target_name': 'gfx_view_jni_headers', - 'type': 'none', - 'variables': { - 'jni_gen_package': 'ui/gfx', - 'input_java_class': 'android/view/ViewConfiguration.class', - }, - 'includes': [ '../../build/jar_file_jni_generator.gypi' ], - }, - { 'target_name': 'gfx_jni_headers', 'type': 'none', - 'dependencies': [ - 'gfx_view_jni_headers' - ], - 'direct_dependent_settings': { - 'include_dirs': [ - '<(SHARED_INTERMEDIATE_DIR)/ui/gfx', - ], - }, 'sources': [ '../android/java/src/org/chromium/ui/gfx/BitmapHelper.java', '../android/java/src/org/chromium/ui/gfx/DeviceDisplayInfo.java', + '../android/java/src/org/chromium/ui/gfx/ViewConfigurationHelper.java', ], 'variables': { 'jni_gen_package': 'ui/gfx', - 'jni_generator_ptr_type': 'long' }, 'includes': [ '../../build/jni_generator.gypi' ], }, diff --git a/chromium/ui/gfx/gfx_tests.gyp b/chromium/ui/gfx/gfx_tests.gyp new file mode 100644 index 00000000000..dda6d142ef3 --- /dev/null +++ b/chromium/ui/gfx/gfx_tests.gyp @@ -0,0 +1,146 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'variables': { + 'chromium_code': 1, + }, + 'targets': [ + { + 'target_name': 'gfx_unittests', + 'type': '<(gtest_target_type)', + # iOS uses a small subset of ui. common_sources are the only files that + # are built on iOS. + 'common_sources' : [ + 'font_unittest.cc', + 'image/image_family_unittest.cc', + 'image/image_skia_unittest.cc', + 'image/image_unittest.cc', + 'image/image_unittest_util.cc', + 'image/image_unittest_util.h', + 'image/image_unittest_util_ios.mm', + 'image/image_unittest_util_mac.mm', + 'test/run_all_unittests.cc', + 'text_elider_unittest.cc', + 'text_utils_unittest.cc', + ], + 'all_sources': [ + '<@(_common_sources)', + 'animation/animation_container_unittest.cc', + 'animation/animation_unittest.cc', + 'animation/multi_animation_unittest.cc', + 'animation/slide_animation_unittest.cc', + 'animation/tween_unittest.cc', + 'blit_unittest.cc', + 'break_list_unittest.cc', + 'canvas_unittest.cc', + 'codec/jpeg_codec_unittest.cc', + 'codec/png_codec_unittest.cc', + 'color_analysis_unittest.cc', + 'color_utils_unittest.cc', + 'display_unittest.cc', + 'font_list_unittest.cc', + 'geometry/box_unittest.cc', + 'geometry/cubic_bezier_unittest.cc', + 'geometry/insets_unittest.cc', + 'geometry/matrix3_unittest.cc', + 'geometry/point_unittest.cc', + 'geometry/point3_unittest.cc', + 'geometry/quad_unittest.cc', + 'geometry/r_tree_unittest.cc', + 'geometry/rect_unittest.cc', + 'geometry/safe_integer_conversions_unittest.cc', + 'geometry/size_unittest.cc', + 'geometry/vector2d_unittest.cc', + 'geometry/vector3d_unittest.cc', + 'image/image_mac_unittest.mm', + 'image/image_util_unittest.cc', + 'range/range_mac_unittest.mm', + 'range/range_unittest.cc', + 'range/range_win_unittest.cc', + 'sequential_id_generator_unittest.cc', + 'shadow_value_unittest.cc', + 'skbitmap_operations_unittest.cc', + 'skrect_conversion_unittest.cc', + 'transform_util_unittest.cc', + 'utf16_indexing_unittest.cc', + ], + 'dependencies': [ + '../../base/base.gyp:base', + '../../base/base.gyp:test_support_base', + '../../skia/skia.gyp:skia', + '../../testing/gtest.gyp:gtest', + '../../third_party/libpng/libpng.gyp:libpng', + '../base/ui_base.gyp:ui_base', + 'gfx.gyp:gfx', + 'gfx.gyp:gfx_geometry', + 'gfx.gyp:gfx_test_support', + ], + 'conditions': [ + ['OS == "ios"', { + 'sources': ['<@(_common_sources)'], + }, { # OS != "ios" + 'sources': ['<@(_all_sources)'], + }], + ['OS == "win"', { + # TODO(jschuh): crbug.com/167187 fix size_t to int truncations. + 'msvs_disabled_warnings': [ 4267, ], + }], + ['OS != "mac" and OS != "ios"', { + 'sources': [ + 'transform_unittest.cc', + 'interpolated_transform_unittest.cc', + ], + }], + ['use_pango == 1', { + 'dependencies': [ + '../../build/linux/system.gyp:fontconfig', + '../../build/linux/system.gyp:pangocairo', + ], + 'sources': [ + 'platform_font_pango_unittest.cc', + ], + 'conditions': [ + ['use_allocator!="none"', { + 'dependencies': [ + '../../base/allocator/allocator.gyp:allocator', + ], + }], + ], + }], + ['use_ozone==1 and use_pango==0', { + 'sources!': [ + 'canvas_unittest.cc', + 'font_list_unittest.cc', + 'font_unittest.cc', + 'text_elider_unittest.cc', + ], + }], + ['OS == "android"', { + 'dependencies': [ + '../../testing/android/native_test.gyp:native_test_native_code', + ], + }], + ], + } + ], + 'conditions': [ + ['OS == "android"', { + 'targets': [ + { + 'target_name': 'gfx_unittests_apk', + 'type': 'none', + 'dependencies': [ + '../android/ui_android.gyp:ui_java', + 'gfx_unittests', + ], + 'variables': { + 'test_suite_name': 'gfx_unittests', + }, + 'includes': [ '../../build/apk_test.gypi' ], + }, + ], + }], + ], +} diff --git a/chromium/ui/gfx/gpu_memory_buffer.cc b/chromium/ui/gfx/gpu_memory_buffer.cc index c1de26c374b..057e8c7575a 100644 --- a/chromium/ui/gfx/gpu_memory_buffer.cc +++ b/chromium/ui/gfx/gpu_memory_buffer.cc @@ -6,6 +6,20 @@ namespace gfx { +GpuMemoryBufferHandle::GpuMemoryBufferHandle() + : type(EMPTY_BUFFER), + handle(base::SharedMemory::NULLHandle()) +#if defined(OS_MACOSX) + , + io_surface_id(0u) +#endif +#if defined(OS_ANDROID) + , + native_buffer(NULL) +#endif +{ +} + GpuMemoryBuffer::GpuMemoryBuffer() {} GpuMemoryBuffer::~GpuMemoryBuffer() {} diff --git a/chromium/ui/gfx/gpu_memory_buffer.h b/chromium/ui/gfx/gpu_memory_buffer.h index 4a7b142d175..1a622016c7c 100644 --- a/chromium/ui/gfx/gpu_memory_buffer.h +++ b/chromium/ui/gfx/gpu_memory_buffer.h @@ -18,61 +18,63 @@ namespace gfx { enum GpuMemoryBufferType { EMPTY_BUFFER, SHARED_MEMORY_BUFFER, - EGL_CLIENT_BUFFER, - IO_SURFACE_BUFFER + IO_SURFACE_BUFFER, + ANDROID_NATIVE_BUFFER, + SURFACE_TEXTURE_BUFFER, + GPU_MEMORY_BUFFER_TYPE_LAST = SURFACE_TEXTURE_BUFFER }; -struct GpuMemoryBufferHandle { - GpuMemoryBufferHandle() - : type(EMPTY_BUFFER), - handle(base::SharedMemory::NULLHandle()) +// TODO(alexst): Merge this with GpuMemoryBufferId as part of switchover to +// the new API for GpuMemoryBuffer allocation when it's done. #if defined(OS_ANDROID) - , native_buffer(NULL) -#endif -#if defined(OS_MACOSX) - , io_surface_id(0) +struct SurfaceTextureId { + SurfaceTextureId() : primary_id(0), secondary_id(0) {} + SurfaceTextureId(int32 primary_id, int32 secondary_id) + : primary_id(primary_id), secondary_id(secondary_id) {} + int32 primary_id; + int32 secondary_id; +}; #endif - { - } + +struct GpuMemoryBufferId { + GpuMemoryBufferId() : primary_id(0), secondary_id(0) {} + GpuMemoryBufferId(int32 primary_id, int32 secondary_id) + : primary_id(primary_id), secondary_id(secondary_id) {} + int32 primary_id; + int32 secondary_id; +}; + +struct GFX_EXPORT GpuMemoryBufferHandle { + GpuMemoryBufferHandle(); bool is_null() const { return type == EMPTY_BUFFER; } GpuMemoryBufferType type; base::SharedMemoryHandle handle; -#if defined(OS_ANDROID) - EGLClientBuffer native_buffer; -#endif + GpuMemoryBufferId global_id; #if defined(OS_MACOSX) uint32 io_surface_id; #endif - +#if defined(OS_ANDROID) + EGLClientBuffer native_buffer; + SurfaceTextureId surface_texture_id; +#endif }; -// Interface for creating and accessing a zero-copy GPU memory buffer. -// This design evolved from the generalization of GraphicBuffer API -// of Android framework. -// -// THREADING CONSIDERATIONS: -// -// This interface is thread-safe. However, multiple threads mapping -// a buffer for Write or ReadOrWrite simultaneously may result in undefined -// behavior and is not allowed. +// This interface typically correspond to a type of shared memory that is also +// shared with the GPU. A GPU memory buffer can be written to directly by +// regular CPU code, but can also be read by the GPU. class GFX_EXPORT GpuMemoryBuffer { public: - enum AccessMode { - READ_ONLY, - WRITE_ONLY, - READ_WRITE, - }; - GpuMemoryBuffer(); virtual ~GpuMemoryBuffer(); - // Maps the buffer so the client can write the bitmap data in |*vaddr| - // subsequently. This call may block, for instance if the hardware needs - // to finish rendering or if CPU caches need to be synchronized. - virtual void Map(AccessMode mode, void** vaddr) = 0; + // Maps the buffer into the client's address space so it can be written to by + // the CPU. This call may block, for instance if the GPU needs to finish + // accessing the buffer or if CPU caches need to be synchronized. Returns NULL + // on failure. + virtual void* Map() = 0; - // Unmaps the buffer. Called after all changes to the buffer are - // completed. + // Unmaps the buffer. It's illegal to use the pointer returned by Map() after + // this has been called. virtual void Unmap() = 0; // Returns true iff the buffer is mapped. diff --git a/chromium/ui/gfx/gtk_compat.h b/chromium/ui/gfx/gtk_compat.h deleted file mode 100644 index ca14a27627a..00000000000 --- a/chromium/ui/gfx/gtk_compat.h +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_GFX_GTK_COMPAT_H_ -#define UI_GFX_GTK_COMPAT_H_ - -#include <gtk/gtk.h> - -// Google Chrome must depend on GTK 2.18, at least until the next LTS drops -// (and we might have to extend which version of GTK we want to target due to -// RHEL). To make our porting job for GTK3 easier, we define all the methods -// that replace deprecated APIs in this file and then include it everywhere. -// -// This file is organized first by version, and then with each version, -// alphabetically by method. -// -// For Google Chrome builds, we want to support RHEL 6, which uses GTK 2.18, -// but the official builder is Ubuntu Lucid with GTK 2.20. Thus for Google -// Chrome builds, we define the GTK 2.20.0 compatibility functions even though -// the system GTK provides the functions. - -#if !GTK_CHECK_VERSION(2, 20, 0) || defined(GOOGLE_CHROME_BUILD) -inline gboolean gtk_widget_get_mapped(GtkWidget* widget) { - return GTK_WIDGET_MAPPED(widget); -} - -inline gboolean gtk_widget_get_realized(GtkWidget* widget) { - return GTK_WIDGET_REALIZED(widget); -} - -inline gboolean gtk_widget_is_toplevel(GtkWidget* widget) { - return GTK_WIDGET_TOPLEVEL(widget); -} - -inline void gtk_widget_set_mapped(GtkWidget* widget, - gboolean mapped) { - if (mapped) - GTK_WIDGET_SET_FLAGS(widget, GTK_MAPPED); - else - GTK_WIDGET_UNSET_FLAGS(widget, GTK_MAPPED); -} - -inline void gtk_widget_set_realized(GtkWidget* widget, - gboolean realized) { - if (realized) - GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED); - else - GTK_WIDGET_UNSET_FLAGS(widget, GTK_REALIZED); -} - -inline void gtk_widget_style_attach(GtkWidget* widget) { - widget->style = gtk_style_attach(widget->style, widget->window); -} -#endif // !GTK_CHECK_VERSION(2, 20, 0) || defined(GOOGLE_CHROME_BUILD) - -#if !GTK_CHECK_VERSION(2, 22, 0) -inline GdkWindow* gdk_drag_context_get_source_window(GdkDragContext *context) { - return context->source_window; -} - -inline gint gdk_visual_get_depth(GdkVisual* visual) { - return visual->depth; -} - -inline GdkWindow* gtk_button_get_event_window(GtkButton* button) { - return button->event_window; -} -#endif // !GTK_CHECK_VERSION(2, 22, 0) - -#if !GTK_CHECK_VERSION(2, 24, 0) -inline void gdk_pixmap_get_size(GdkPixmap* pixmap, gint* width, gint* height) { - gdk_drawable_get_size(GDK_DRAWABLE(pixmap), width, height); -} - -inline GdkDisplay* gdk_window_get_display(GdkWindow* window) { - return gdk_drawable_get_display(GDK_DRAWABLE(window)); -} - -inline int gdk_window_get_height(GdkWindow* window) { - int height; - gdk_drawable_get_size(GDK_DRAWABLE(window), NULL, &height); - return height; -} - -inline GdkScreen* gdk_window_get_screen(GdkWindow* window) { - return gdk_drawable_get_screen(GDK_DRAWABLE(window)); -} - -inline int gdk_window_get_width(GdkWindow* window) { - int width; - gdk_drawable_get_size(GDK_DRAWABLE(window), &width, NULL); - return width; -} -#endif // !GTK_CHECK_VERSION(2, 24, 0) - -#endif // UI_GFX_GTK_COMPAT_H_ diff --git a/chromium/ui/gfx/gtk_native_view_id_manager.cc b/chromium/ui/gfx/gtk_native_view_id_manager.cc deleted file mode 100644 index 0844b190dd9..00000000000 --- a/chromium/ui/gfx/gtk_native_view_id_manager.cc +++ /dev/null @@ -1,254 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/gfx/gtk_native_view_id_manager.h" - -#include <gdk/gdkx.h> -#include <gtk/gtk.h> - -#include "base/logging.h" -#include "base/memory/singleton.h" -#include "base/rand_util.h" -#include "ui/gfx/gdk_compat.h" -#include "ui/gfx/gtk_compat.h" -#include "ui/gfx/gtk_preserve_window.h" - -// ----------------------------------------------------------------------------- -// Bounce functions for GTK to callback into a C++ object... - -void OnRealize(gfx::NativeView widget, void* arg) { - GtkNativeViewManager* manager = reinterpret_cast<GtkNativeViewManager*>(arg); - manager->OnRealize(widget); -} - -void OnUnrealize(gfx::NativeView widget, void *arg) { - GtkNativeViewManager* manager = reinterpret_cast<GtkNativeViewManager*>(arg); - manager->OnUnrealize(widget); -} - -static void OnDestroy(GtkObject* obj, void* arg) { - GtkNativeViewManager* manager = reinterpret_cast<GtkNativeViewManager*>(arg); - manager->OnDestroy(reinterpret_cast<GtkWidget*>(obj)); -} - -// ----------------------------------------------------------------------------- - - -// ----------------------------------------------------------------------------- -// Public functions... - -GtkNativeViewManager::GtkNativeViewManager() { -} - -GtkNativeViewManager::~GtkNativeViewManager() { -} - -// static -GtkNativeViewManager* GtkNativeViewManager::GetInstance() { - return Singleton<GtkNativeViewManager>::get(); -} - -gfx::NativeViewId GtkNativeViewManager::GetIdForWidget(gfx::NativeView widget) { - // This is just for unit tests: - if (!widget) - return 0; - - base::AutoLock locked(lock_); - - std::map<gfx::NativeView, gfx::NativeViewId>::const_iterator i = - native_view_to_id_.find(widget); - - if (i != native_view_to_id_.end()) - return i->second; - - gfx::NativeViewId new_id = - static_cast<gfx::NativeViewId>(base::RandUint64()); - while (id_to_info_.find(new_id) != id_to_info_.end()) - new_id = static_cast<gfx::NativeViewId>(base::RandUint64()); - - NativeViewInfo info; - info.widget = widget; - if (gtk_widget_get_realized(widget)) { - GdkWindow *gdk_window = gtk_widget_get_window(widget); - DCHECK(gdk_window); - info.x_window_id = GDK_WINDOW_XID(gdk_window); - } - - native_view_to_id_[widget] = new_id; - id_to_info_[new_id] = info; - - g_signal_connect(widget, "realize", G_CALLBACK(::OnRealize), this); - g_signal_connect(widget, "unrealize", G_CALLBACK(::OnUnrealize), this); - g_signal_connect(widget, "destroy", G_CALLBACK(::OnDestroy), this); - - return new_id; -} - -bool GtkNativeViewManager::GetXIDForId(XID* output, gfx::NativeViewId id) { - base::AutoLock locked(lock_); - - std::map<gfx::NativeViewId, NativeViewInfo>::const_iterator i = - id_to_info_.find(id); - - if (i == id_to_info_.end()) - return false; - - *output = i->second.x_window_id; - return true; -} - -bool GtkNativeViewManager::GetNativeViewForId(gfx::NativeView* output, - gfx::NativeViewId id) { - base::AutoLock locked(lock_); - - std::map<gfx::NativeViewId, NativeViewInfo>::const_iterator i = - id_to_info_.find(id); - - if (i == id_to_info_.end()) - return false; - - *output = i->second.widget; - return true; -} - -bool GtkNativeViewManager::GetPermanentXIDForId(XID* output, - gfx::NativeViewId id) { - base::AutoLock locked(lock_); - - std::map<gfx::NativeViewId, NativeViewInfo>::iterator i = - id_to_info_.find(id); - - if (i == id_to_info_.end()) - return false; - - // We only return permanent XIDs for widgets that allow us to guarantee that - // the XID will not change. - DCHECK(GTK_IS_PRESERVE_WINDOW(i->second.widget)); - GtkPreserveWindow* widget = - reinterpret_cast<GtkPreserveWindow*>(i->second.widget); - gtk_preserve_window_set_preserve(widget, TRUE); - - *output = GDK_WINDOW_XID(gtk_widget_get_window(i->second.widget)); - - // Update the reference count on the permanent XID. - PermanentXIDInfo info; - info.widget = widget; - info.ref_count = 1; - std::pair<std::map<XID, PermanentXIDInfo>::iterator, bool> ret = - perm_xid_to_info_.insert(std::make_pair(*output, info)); - - if (!ret.second) { - DCHECK(ret.first->second.widget == widget); - ret.first->second.ref_count++; - } - - return true; -} - -bool GtkNativeViewManager::AddRefPermanentXID(XID xid) { - base::AutoLock locked(lock_); - - std::map<XID, PermanentXIDInfo>::iterator i = - perm_xid_to_info_.find(xid); - - if (i == perm_xid_to_info_.end()) - return false; - - i->second.ref_count++; - - return true; -} - -void GtkNativeViewManager::ReleasePermanentXID(XID xid) { - base::AutoLock locked(lock_); - - std::map<XID, PermanentXIDInfo>::iterator i = - perm_xid_to_info_.find(xid); - - if (i == perm_xid_to_info_.end()) - return; - - if (i->second.ref_count > 1) { - i->second.ref_count--; - } else { - if (i->second.widget) { - gtk_preserve_window_set_preserve(i->second.widget, FALSE); - } else { - GdkWindow* window = reinterpret_cast<GdkWindow*>( - gdk_x11_window_lookup_for_display(gdk_display_get_default(), xid)); - DCHECK(window); - gdk_window_destroy(window); - } - perm_xid_to_info_.erase(i); - } -} - -// ----------------------------------------------------------------------------- - - -// ----------------------------------------------------------------------------- -// Private functions... - -gfx::NativeViewId GtkNativeViewManager::GetWidgetId(gfx::NativeView widget) { - lock_.AssertAcquired(); - - std::map<gfx::NativeView, gfx::NativeViewId>::const_iterator i = - native_view_to_id_.find(widget); - - CHECK(i != native_view_to_id_.end()); - return i->second; -} - -void GtkNativeViewManager::OnRealize(gfx::NativeView widget) { - base::AutoLock locked(lock_); - - const gfx::NativeViewId id = GetWidgetId(widget); - std::map<gfx::NativeViewId, NativeViewInfo>::iterator i = - id_to_info_.find(id); - - CHECK(i != id_to_info_.end()); - - GdkWindow* gdk_window = gtk_widget_get_window(widget); - CHECK(gdk_window); - i->second.x_window_id = GDK_WINDOW_XID(gdk_window); -} - -void GtkNativeViewManager::OnUnrealize(gfx::NativeView widget) { - base::AutoLock locked(lock_); - - const gfx::NativeViewId id = GetWidgetId(widget); - std::map<gfx::NativeViewId, NativeViewInfo>::iterator i = - id_to_info_.find(id); - - CHECK(i != id_to_info_.end()); -} - -void GtkNativeViewManager::OnDestroy(gfx::NativeView widget) { - base::AutoLock locked(lock_); - - std::map<gfx::NativeView, gfx::NativeViewId>::iterator i = - native_view_to_id_.find(widget); - CHECK(i != native_view_to_id_.end()); - - std::map<gfx::NativeViewId, NativeViewInfo>::iterator j = - id_to_info_.find(i->second); - CHECK(j != id_to_info_.end()); - - // If the XID is supposed to outlive the widget, mark it - // in the lookup table. - if (GTK_IS_PRESERVE_WINDOW(widget) && - gtk_preserve_window_get_preserve( - reinterpret_cast<GtkPreserveWindow*>(widget))) { - std::map<XID, PermanentXIDInfo>::iterator k = - perm_xid_to_info_.find(GDK_WINDOW_XID(gtk_widget_get_window(widget))); - - if (k != perm_xid_to_info_.end()) - k->second.widget = NULL; - } - - native_view_to_id_.erase(i); - id_to_info_.erase(j); -} - -// ----------------------------------------------------------------------------- diff --git a/chromium/ui/gfx/gtk_native_view_id_manager.h b/chromium/ui/gfx/gtk_native_view_id_manager.h deleted file mode 100644 index 44219209273..00000000000 --- a/chromium/ui/gfx/gtk_native_view_id_manager.h +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_GFX_GTK_NATIVE_VIEW_ID_MANAGER_H_ -#define UI_GFX_GTK_NATIVE_VIEW_ID_MANAGER_H_ - -#include <map> - -#include "base/synchronization/lock.h" -#include "ui/gfx/gfx_export.h" -#include "ui/gfx/native_widget_types.h" - -template <typename T> struct DefaultSingletonTraits; - -typedef unsigned long XID; -struct _GtkPreserveWindow; - -// NativeViewIds are the opaque values which the renderer holds as a reference -// to a window. -// -// We could make NativeViewIds be the X id of the window. However, at the -// time when we need to tell the renderer about its NativeViewId, an XID isn't -// availible and it goes very much against the grain of the code to make it so. -// Also, we worry that GTK might choose to change the underlying X window id -// when, say, the widget is hidden or repacked. Finally, if we used XIDs then a -// compromised renderer could start asking questions about any X windows on the -// system. -// -// Thus, we have this object. It produces random NativeViewIds from GtkWidget -// pointers and observes the various signals from the widget for when an X -// window is created, destroyed etc. Thus it provides a thread safe mapping -// from NativeViewIds to the current XID for that widget. -class GFX_EXPORT GtkNativeViewManager { - public: - // Returns the singleton instance. - static GtkNativeViewManager* GetInstance(); - - // Must be called from the UI thread: - // - // Return a NativeViewId for the given widget and attach to the various - // signals emitted by that widget. The NativeViewId is pseudo-randomly - // allocated so that a compromised renderer trying to guess values will fail - // with high probability. The NativeViewId will not be reused for the - // lifetime of the GtkWidget. - gfx::NativeViewId GetIdForWidget(gfx::NativeView widget); - - // May be called from any thread: - // - // xid: (output) the resulting X window ID, or 0 - // id: a value previously returned from GetIdForWidget - // returns: true if |id| is a valid id, false otherwise. - // - // If the widget referenced by |id| does not current have an X window id, - // |*xid| is set to 0. - bool GetXIDForId(XID* xid, gfx::NativeViewId id); - - // May be called from the UI thread: - // - // Same as GetXIDForId except it returns the NativeView (GtkWidget*). - bool GetNativeViewForId(gfx::NativeView* xid, gfx::NativeViewId id); - - // Must be called from the UI thread because we may need the associated - // widget to create a window. - // - // Keeping the XID permanent requires a bit of overhead, so it must - // be explicitly requested. - // - // xid: (output) the resulting X window - // id: a value previously returned from GetIdForWidget - // returns: true if |id| is a valid id, false otherwise. - bool GetPermanentXIDForId(XID* xid, gfx::NativeViewId id); - - // Can be called from any thread. - // Will return false if the given XID isn't permanent or has already been - // released. - bool AddRefPermanentXID(XID xid); - - // Must be called from the UI thread because we may need to access a - // GtkWidget or destroy a GdkWindow. - // - // If the widget associated with the XID is still alive, allow the widget - // to destroy the associated XID when it wants. Otherwise, destroy the - // GdkWindow associated with the XID. - void ReleasePermanentXID(XID xid); - - // These are actually private functions, but need to be called from statics. - void OnRealize(gfx::NativeView widget); - void OnUnrealize(gfx::NativeView widget); - void OnDestroy(gfx::NativeView widget); - - private: - // This object is a singleton: - GtkNativeViewManager(); - ~GtkNativeViewManager(); - friend struct DefaultSingletonTraits<GtkNativeViewManager>; - - struct NativeViewInfo { - NativeViewInfo() : widget(NULL), x_window_id(0) { - } - gfx::NativeView widget; - XID x_window_id; - }; - - gfx::NativeViewId GetWidgetId(gfx::NativeView id); - - // protects native_view_to_id_ and id_to_info_ - base::Lock lock_; - - // If asked for an id for the same widget twice, we want to return the same - // id. So this records the current mapping. - std::map<gfx::NativeView, gfx::NativeViewId> native_view_to_id_; - std::map<gfx::NativeViewId, NativeViewInfo> id_to_info_; - - struct PermanentXIDInfo { - PermanentXIDInfo() : widget(NULL), ref_count(0) { - } - _GtkPreserveWindow* widget; - int ref_count; - }; - - // Used to maintain the reference count for permanent XIDs - // (referenced by GetPermanentXIDForId and dereferenced by - // ReleasePermanentXID). Only those XIDs with a positive reference count - // will be in the table. - // - // In general, several GTK widgets may share the same X window. We assume - // that is not true of the widgets stored in this registry. - // - // An XID will map to NULL, if there is an outstanding reference but the - // widget was destroyed. In this case, the destruction of the X window - // is deferred to the dropping of all references. - std::map<XID, PermanentXIDInfo> perm_xid_to_info_; - - DISALLOW_COPY_AND_ASSIGN(GtkNativeViewManager); -}; - -#endif // UI_GFX_GTK_NATIVE_VIEW_ID_MANAGER_H_ diff --git a/chromium/ui/gfx/gtk_preserve_window.cc b/chromium/ui/gfx/gtk_preserve_window.cc deleted file mode 100644 index 78a49c28510..00000000000 --- a/chromium/ui/gfx/gtk_preserve_window.cc +++ /dev/null @@ -1,264 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/gfx/gtk_preserve_window.h" - -#include <gdk/gdk.h> -#include <gtk/gtk.h> - -#include "ui/gfx/gtk_compat.h" - -G_BEGIN_DECLS - -#define GTK_PRESERVE_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \ - GTK_TYPE_PRESERVE_WINDOW, \ - GtkPreserveWindowPrivate)) - -typedef struct _GtkPreserveWindowPrivate GtkPreserveWindowPrivate; - -struct _GtkPreserveWindowPrivate { - // If true, don't create/destroy windows on realize/unrealize. - gboolean preserve_window; - - // Whether or not we delegate the resize of the GdkWindow - // to someone else. - gboolean delegate_resize; - - // Accessible factory and userdata. - AtkObject* (*accessible_factory)(void* userdata); - void* accessible_factory_userdata; -}; - -G_DEFINE_TYPE(GtkPreserveWindow, gtk_preserve_window, GTK_TYPE_FIXED) - -static void gtk_preserve_window_destroy(GtkObject* object); -static void gtk_preserve_window_realize(GtkWidget* widget); -static void gtk_preserve_window_unrealize(GtkWidget* widget); -static void gtk_preserve_window_size_allocate(GtkWidget* widget, - GtkAllocation* allocation); -static AtkObject* gtk_preserve_window_get_accessible(GtkWidget* widget); - -static void gtk_preserve_window_class_init(GtkPreserveWindowClass *klass) { - GtkWidgetClass* widget_class = reinterpret_cast<GtkWidgetClass*>(klass); - widget_class->realize = gtk_preserve_window_realize; - widget_class->unrealize = gtk_preserve_window_unrealize; - widget_class->size_allocate = gtk_preserve_window_size_allocate; - widget_class->get_accessible = gtk_preserve_window_get_accessible; - - GtkObjectClass* object_class = reinterpret_cast<GtkObjectClass*>(klass); - object_class->destroy = gtk_preserve_window_destroy; - - GObjectClass* gobject_class = G_OBJECT_CLASS(klass); - g_type_class_add_private(gobject_class, sizeof(GtkPreserveWindowPrivate)); -} - -static void gtk_preserve_window_init(GtkPreserveWindow* widget) { - GtkPreserveWindowPrivate* priv = GTK_PRESERVE_WINDOW_GET_PRIVATE(widget); - priv->preserve_window = FALSE; - priv->accessible_factory = NULL; - priv->accessible_factory_userdata = NULL; - - // These widgets always have their own window. - gtk_widget_set_has_window(GTK_WIDGET(widget), TRUE); -} - -GtkWidget* gtk_preserve_window_new() { - return GTK_WIDGET(g_object_new(GTK_TYPE_PRESERVE_WINDOW, NULL)); -} - -static void gtk_preserve_window_destroy(GtkObject* object) { - GtkWidget* widget = reinterpret_cast<GtkWidget*>(object); - GtkPreserveWindowPrivate* priv = GTK_PRESERVE_WINDOW_GET_PRIVATE(widget); - - GdkWindow* gdk_window = gtk_widget_get_window(widget); - if (gdk_window) { - gdk_window_set_user_data(gdk_window, NULL); - // If the window is preserved, someone else must destroy it. - if (!priv->preserve_window) - gdk_window_destroy(gdk_window); - gtk_widget_set_window(widget, NULL); - } - - GTK_OBJECT_CLASS(gtk_preserve_window_parent_class)->destroy(object); -} - -static void gtk_preserve_window_realize(GtkWidget* widget) { - g_return_if_fail(GTK_IS_PRESERVE_WINDOW(widget)); - - GdkWindow* gdk_window = gtk_widget_get_window(widget); - if (gdk_window) { - GtkAllocation allocation; - gtk_widget_get_allocation(widget, &allocation); - - gdk_window_reparent(gdk_window, - gtk_widget_get_parent_window(widget), - allocation.x, - allocation.y); - GtkPreserveWindowPrivate* priv = GTK_PRESERVE_WINDOW_GET_PRIVATE(widget); - if (!priv->delegate_resize) { - gdk_window_resize(gdk_window, - allocation.width, - allocation.height); - } - gint event_mask = gtk_widget_get_events(widget); - event_mask |= GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK; - gdk_window_set_events(gdk_window, (GdkEventMask) event_mask); - gdk_window_set_user_data(gdk_window, widget); - - gtk_widget_set_realized(widget, TRUE); - - gtk_widget_style_attach(widget); - gtk_style_set_background(gtk_widget_get_style(widget), - gdk_window, GTK_STATE_NORMAL); - } else { - GTK_WIDGET_CLASS(gtk_preserve_window_parent_class)->realize(widget); - } -} - -static void gtk_preserve_window_unrealize(GtkWidget* widget) { - g_return_if_fail(GTK_IS_PRESERVE_WINDOW(widget)); - - GtkPreserveWindowPrivate* priv = GTK_PRESERVE_WINDOW_GET_PRIVATE(widget); - if (priv->preserve_window) { - GtkWidgetClass* widget_class = - GTK_WIDGET_CLASS(gtk_preserve_window_parent_class); - GtkContainerClass* container_class = - GTK_CONTAINER_CLASS(gtk_preserve_window_parent_class); - - if (gtk_widget_get_mapped(widget)) { - widget_class->unmap(widget); - - gtk_widget_set_mapped(widget, FALSE); - } - - // This is the behavior from GtkWidget, inherited by GtkFixed. - // It is unclear why we should not call the potentially overridden - // unrealize method (via the callback), but doing so causes errors. - container_class->forall( - GTK_CONTAINER(widget), FALSE, - reinterpret_cast<GtkCallback>(gtk_widget_unrealize), NULL); - - GdkWindow* gdk_window = gtk_widget_get_window(widget); - - // TODO(erg): Almost all style handling will need to be overhauled in GTK3. - gtk_style_detach(gtk_widget_get_style(widget)); - gdk_window_reparent(gdk_window, gdk_get_default_root_window(), 0, 0); - gtk_selection_remove_all(widget); - gdk_window_set_user_data(gdk_window, NULL); - - gtk_widget_set_realized(widget, FALSE); - } else { - GTK_WIDGET_CLASS(gtk_preserve_window_parent_class)->unrealize(widget); - } -} - -gboolean gtk_preserve_window_get_preserve(GtkPreserveWindow* window) { - g_return_val_if_fail(GTK_IS_PRESERVE_WINDOW(window), FALSE); - GtkPreserveWindowPrivate* priv = GTK_PRESERVE_WINDOW_GET_PRIVATE(window); - - return priv->preserve_window; -} - -void gtk_preserve_window_set_preserve(GtkPreserveWindow* window, - gboolean value) { - g_return_if_fail(GTK_IS_PRESERVE_WINDOW(window)); - GtkPreserveWindowPrivate* priv = GTK_PRESERVE_WINDOW_GET_PRIVATE(window); - priv->preserve_window = value; - - GtkWidget* widget = GTK_WIDGET(window); - GdkWindow* gdk_window = gtk_widget_get_window(widget); - if (value && !gdk_window) { - GdkWindowAttr attributes; - gint attributes_mask; - - // We may not know the width and height, so we rely on the fact - // that a size-allocation will resize it later. - attributes.width = 1; - attributes.height = 1; - - attributes.window_type = GDK_WINDOW_CHILD; - attributes.wclass = GDK_INPUT_OUTPUT; - attributes.override_redirect = TRUE; - - attributes.visual = gtk_widget_get_visual(widget); - attributes.colormap = gtk_widget_get_colormap(widget); - - attributes.event_mask = gtk_widget_get_events(widget); - attributes.event_mask |= GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK; - - attributes_mask = GDK_WA_VISUAL | GDK_WA_COLORMAP | GDK_WA_NOREDIR; - gdk_window = gdk_window_new( - gdk_get_default_root_window(), &attributes, attributes_mask); - gtk_widget_set_window(widget, gdk_window); - } else if (!value && gdk_window && !gtk_widget_get_realized(widget)) { - gdk_window_destroy(gdk_window); - gtk_widget_set_window(widget, NULL); - } -} - -void gtk_preserve_window_size_allocate(GtkWidget* widget, - GtkAllocation* allocation) { - g_return_if_fail(GTK_IS_PRESERVE_WINDOW(widget)); - - gtk_widget_set_allocation(widget, allocation); - - if (gtk_widget_get_realized(widget)) { - GtkPreserveWindowPrivate* priv = GTK_PRESERVE_WINDOW_GET_PRIVATE(widget); - GdkWindow* gdk_window = gtk_widget_get_window(widget); - if (priv->delegate_resize) { - gdk_window_move(gdk_window, allocation->x, allocation->y); - } else { - gdk_window_move_resize( - gdk_window, allocation->x, allocation->y, - allocation->width, allocation->height); - } - } - - // Propagate resize to children - guint16 border_width = gtk_container_get_border_width(GTK_CONTAINER(widget)); - GList *children = GTK_FIXED(widget)->children; - while (children) { - GtkFixedChild *child = reinterpret_cast<GtkFixedChild*>(children->data); - if (gtk_widget_get_visible(child->widget)) { - GtkRequisition child_requisition; - gtk_widget_get_child_requisition(child->widget, &child_requisition); - - GtkAllocation child_allocation; - child_allocation.x = child->x + border_width; - child_allocation.y = child->y + border_width; - child_allocation.width = child_requisition.width; - child_allocation.height = child_requisition.height; - - gtk_widget_size_allocate(child->widget, &child_allocation); - } - children = children->next; - } -} - -void gtk_preserve_window_delegate_resize(GtkPreserveWindow* widget, - gboolean delegate) { - GtkPreserveWindowPrivate* priv = GTK_PRESERVE_WINDOW_GET_PRIVATE(widget); - priv->delegate_resize = delegate; -} - -void gtk_preserve_window_set_accessible_factory( - GtkPreserveWindow* widget, - AtkObject* (*factory)(void* userdata), - gpointer userdata) { - GtkPreserveWindowPrivate* priv = GTK_PRESERVE_WINDOW_GET_PRIVATE(widget); - priv->accessible_factory = factory; - priv->accessible_factory_userdata = userdata; -} - -AtkObject* gtk_preserve_window_get_accessible(GtkWidget* widget) { - GtkPreserveWindowPrivate* priv = GTK_PRESERVE_WINDOW_GET_PRIVATE(widget); - if (priv->accessible_factory) { - return priv->accessible_factory(priv->accessible_factory_userdata); - } else { - return GTK_WIDGET_CLASS(gtk_preserve_window_parent_class) - ->get_accessible(widget); - } -} - -G_END_DECLS diff --git a/chromium/ui/gfx/gtk_preserve_window.h b/chromium/ui/gfx/gtk_preserve_window.h deleted file mode 100644 index 5b5198bf56f..00000000000 --- a/chromium/ui/gfx/gtk_preserve_window.h +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_GFX_GTK_PRESERVE_WINDOW_H_ -#define UI_GFX_GTK_PRESERVE_WINDOW_H_ - -#include <atk/atk.h> -#include <gdk/gdk.h> -#include <gtk/gtk.h> - -#include "ui/gfx/gfx_export.h" - -// GtkFixed creates an X window when realized and destroys an X window -// when unrealized. GtkPreserveWindow allows overrides this -// behaviour. When preserve is set (via gtk_preserve_window_set_preserve), -// the X window is only destroyed when the widget is destroyed. - -G_BEGIN_DECLS - -#define GTK_TYPE_PRESERVE_WINDOW \ - (gtk_preserve_window_get_type()) -#define GTK_PRESERVE_WINDOW(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), GTK_TYPE_PRESERVE_WINDOW, \ - GtkPreserveWindow)) -#define GTK_PRESERVE_WINDOW_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass), GTK_TYPE_PRESERVE_WINDOW, \ - GtkPreserveWindowClass)) -#define GTK_IS_PRESERVE_WINDOW(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj), GTK_TYPE_PRESERVE_WINDOW)) -#define GTK_IS_PRESERVE_WINDOW_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass), GTK_TYPE_PRESERVE_WINDOW)) -#define GTK_PRESERVE_WINDOW_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS((obj), GTK_TYPE_PRESERVE_WINDOW, \ - GtkPreserveWindowClass)) - -typedef struct _GtkPreserveWindow GtkPreserveWindow; -typedef struct _GtkPreserveWindowClass GtkPreserveWindowClass; - -struct _GtkPreserveWindow { - // Parent class. - GtkFixed fixed; -}; - -struct _GtkPreserveWindowClass { - GtkFixedClass parent_class; -}; - -GFX_EXPORT GType gtk_preserve_window_get_type() G_GNUC_CONST; -GFX_EXPORT GtkWidget* gtk_preserve_window_new(); - -// Whether or not we should preserve associated windows as the widget -// is realized or unrealized. -GFX_EXPORT gboolean gtk_preserve_window_get_preserve(GtkPreserveWindow* widget); -GFX_EXPORT void gtk_preserve_window_set_preserve(GtkPreserveWindow* widget, - gboolean value); - -// Whether or not someone else will gdk_window_resize the GdkWindow associated -// with this widget (needed by the GPU process to synchronize resizing -// with swapped between front and back buffer). -GFX_EXPORT void gtk_preserve_window_delegate_resize(GtkPreserveWindow* widget, - gboolean delegate); - -// Provide a function to return an AtkObject* when calls to get_accessible -// are made on this widget. The parameter |userdata| will be passed to the -// factory function. -GFX_EXPORT void gtk_preserve_window_set_accessible_factory( - GtkPreserveWindow* widget, - AtkObject* (*factory)(void* userdata), - gpointer userdata); - -G_END_DECLS - -#endif // UI_GFX_GTK_PRESERVE_WINDOW_H_ diff --git a/chromium/ui/gfx/gtk_util.cc b/chromium/ui/gfx/gtk_util.cc deleted file mode 100644 index 0c111687572..00000000000 --- a/chromium/ui/gfx/gtk_util.cc +++ /dev/null @@ -1,190 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/gfx/gtk_util.h" - -#include <gdk/gdk.h> -#include <gtk/gtk.h> -#include <stdlib.h> - -#include "base/basictypes.h" -#include "base/command_line.h" -#include "base/memory/scoped_ptr.h" -#include "third_party/skia/include/core/SkBitmap.h" -#include "third_party/skia/include/core/SkUnPreMultiply.h" -#include "ui/gfx/rect.h" - -namespace { - -// A process wide singleton that manages our usage of gdk cursors. -// gdk_cursor_new() hits the disk in several places and GdkCursor instances can -// be reused throughout the process. -class GdkCursorCache { - public: - GdkCursorCache() {} - ~GdkCursorCache() { - for (GdkCursorMap::iterator i(cursors_.begin()); i != cursors_.end(); ++i) { - gdk_cursor_unref(i->second); - } - cursors_.clear(); - } - - GdkCursor* GetCursorImpl(GdkCursorType type) { - GdkCursorMap::iterator it = cursors_.find(type); - GdkCursor* cursor = NULL; - if (it == cursors_.end()) { - cursor = gdk_cursor_new(type); - cursors_.insert(std::make_pair(type, cursor)); - } else { - cursor = it->second; - } - - // It is not necessary to add a reference here. The callers can ref the - // cursor if they need it for something. - return cursor; - } - - private: - typedef std::map<GdkCursorType, GdkCursor*> GdkCursorMap; - GdkCursorMap cursors_; - - DISALLOW_COPY_AND_ASSIGN(GdkCursorCache); -}; - -} // namespace - -namespace gfx { - -static void CommonInitFromCommandLine(const CommandLine& command_line, - void (*init_func)(gint*, gchar***)) { - const std::vector<std::string>& args = command_line.argv(); - int argc = args.size(); - scoped_ptr<char *[]> argv(new char *[argc + 1]); - for (size_t i = 0; i < args.size(); ++i) { - // TODO(piman@google.com): can gtk_init modify argv? Just being safe - // here. - argv[i] = strdup(args[i].c_str()); - } - argv[argc] = NULL; - char **argv_pointer = argv.get(); - - init_func(&argc, &argv_pointer); - for (size_t i = 0; i < args.size(); ++i) { - free(argv[i]); - } -} - -void GtkInitFromCommandLine(const CommandLine& command_line) { - CommonInitFromCommandLine(command_line, gtk_init); -} - -void GdkInitFromCommandLine(const CommandLine& command_line) { - CommonInitFromCommandLine(command_line, gdk_init); -} - -GdkPixbuf* GdkPixbufFromSkBitmap(const SkBitmap& bitmap) { - if (bitmap.isNull()) - return NULL; - - SkAutoLockPixels lock_pixels(bitmap); - - int width = bitmap.width(); - int height = bitmap.height(); - - GdkPixbuf* pixbuf = gdk_pixbuf_new( - GDK_COLORSPACE_RGB, // The only colorspace gtk supports. - TRUE, // There is an alpha channel. - 8, - width, height); - - // SkBitmaps are premultiplied, we need to unpremultiply them. - const int kBytesPerPixel = 4; - uint8* divided = gdk_pixbuf_get_pixels(pixbuf); - - for (int y = 0, i = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - uint32 pixel = bitmap.getAddr32(0, y)[x]; - - int alpha = SkColorGetA(pixel); - if (alpha != 0 && alpha != 255) { - SkColor unmultiplied = SkUnPreMultiply::PMColorToColor(pixel); - divided[i + 0] = SkColorGetR(unmultiplied); - divided[i + 1] = SkColorGetG(unmultiplied); - divided[i + 2] = SkColorGetB(unmultiplied); - divided[i + 3] = alpha; - } else { - divided[i + 0] = SkColorGetR(pixel); - divided[i + 1] = SkColorGetG(pixel); - divided[i + 2] = SkColorGetB(pixel); - divided[i + 3] = alpha; - } - i += kBytesPerPixel; - } - } - - return pixbuf; -} - -void SubtractRectanglesFromRegion(GdkRegion* region, - const std::vector<Rect>& cutouts) { - for (size_t i = 0; i < cutouts.size(); ++i) { - GdkRectangle rect = cutouts[i].ToGdkRectangle(); - GdkRegion* rect_region = gdk_region_rectangle(&rect); - gdk_region_subtract(region, rect_region); - // TODO(deanm): It would be nice to be able to reuse the GdkRegion here. - gdk_region_destroy(rect_region); - } -} - -GdkCursor* GetCursor(int type) { - CR_DEFINE_STATIC_LOCAL(GdkCursorCache, impl, ()); - return impl.GetCursorImpl(static_cast<GdkCursorType>(type)); -} - -void InitRCStyles() { - static const char kRCText[] = - // Make our dialogs styled like the GNOME HIG. - // - // TODO(evanm): content-area-spacing was introduced in a later - // version of GTK, so we need to set that manually on all dialogs. - // Perhaps it would make sense to have a shared FixupDialog() function. - "style \"gnome-dialog\" {\n" - " xthickness = 12\n" - " GtkDialog::action-area-border = 0\n" - " GtkDialog::button-spacing = 6\n" - " GtkDialog::content-area-spacing = 18\n" - " GtkDialog::content-area-border = 12\n" - "}\n" - // Note we set it at the "application" priority, so users can override. - "widget \"GtkDialog\" style : application \"gnome-dialog\"\n" - - // Make our about dialog special, so the image is flush with the edge. - "style \"about-dialog\" {\n" - " GtkDialog::action-area-border = 12\n" - " GtkDialog::button-spacing = 6\n" - " GtkDialog::content-area-spacing = 18\n" - " GtkDialog::content-area-border = 0\n" - "}\n" - "widget \"about-dialog\" style : application \"about-dialog\"\n"; - - gtk_rc_parse_string(kRCText); -} - -base::TimeDelta GetCursorBlinkCycle() { - // From http://library.gnome.org/devel/gtk/unstable/GtkSettings.html, this is - // the default value for gtk-cursor-blink-time. - static const gint kGtkDefaultCursorBlinkTime = 1200; - - gint cursor_blink_time = kGtkDefaultCursorBlinkTime; - gboolean cursor_blink = TRUE; - g_object_get(gtk_settings_get_default(), - "gtk-cursor-blink-time", &cursor_blink_time, - "gtk-cursor-blink", &cursor_blink, - NULL); - return cursor_blink ? - base::TimeDelta::FromMilliseconds(cursor_blink_time) : - base::TimeDelta(); -} - -} // namespace gfx diff --git a/chromium/ui/gfx/gtk_util.h b/chromium/ui/gfx/gtk_util.h deleted file mode 100644 index 91f864bbe4c..00000000000 --- a/chromium/ui/gfx/gtk_util.h +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_GFX_GTK_UTIL_H_ -#define UI_GFX_GTK_UTIL_H_ - -#include <vector> - -#include "base/time/time.h" -#include "ui/gfx/gfx_export.h" - -typedef struct _GdkPixbuf GdkPixbuf; -typedef struct _GdkRegion GdkRegion; -typedef struct _GdkCursor GdkCursor; - -class CommandLine; -class SkBitmap; - -namespace gfx { - -class Rect; - -// Call gtk_init() / gdk_init() using the argc and argv from command_line. -// These init functions want an argc and argv that they can mutate; we provide -// those, but leave the original CommandLine unaltered. -GFX_EXPORT void GtkInitFromCommandLine(const CommandLine& command_line); -GFX_EXPORT void GdkInitFromCommandLine(const CommandLine& command_line); - -// Convert and copy a SkBitmap to a GdkPixbuf. NOTE: this uses BGRAToRGBA, so -// it is an expensive operation. The returned GdkPixbuf will have a refcount of -// 1, and the caller is responsible for unrefing it when done. -GFX_EXPORT GdkPixbuf* GdkPixbufFromSkBitmap(const SkBitmap& bitmap); - -// Modify the given region by subtracting the given rectangles. -GFX_EXPORT void SubtractRectanglesFromRegion(GdkRegion* region, - const std::vector<Rect>& cutouts); - -// Returns a static instance of a GdkCursor* object, sharable across the -// process. Caller must gdk_cursor_ref() it if they want to assume ownership. -GFX_EXPORT GdkCursor* GetCursor(int type); - -// Initialize some GTK settings so that our dialogs are consistent. -GFX_EXPORT void InitRCStyles(); - -// Queries GtkSettings for the cursor blink cycle time. Returns a 0 duration if -// blinking is disabled. -GFX_EXPORT base::TimeDelta GetCursorBlinkCycle(); - -} // namespace gfx - -#endif // UI_GFX_GTK_UTIL_H_ diff --git a/chromium/ui/gfx/icon_util.cc b/chromium/ui/gfx/icon_util.cc index 223618601e3..dd2b9bd6b50 100644 --- a/chromium/ui/gfx/icon_util.cc +++ b/chromium/ui/gfx/icon_util.cc @@ -77,7 +77,7 @@ bool BuildResizedImageFamily(const gfx::ImageFamily& image_family, SkBitmap best_bitmap = best->AsBitmap(); // Only kARGB_8888 images are supported. // This will also filter out images with no pixels. - if (best_bitmap.config() != SkBitmap::kARGB_8888_Config) + if (best_bitmap.colorType() != kPMColor_SkColorType) return false; SkBitmap resized_bitmap = skia::ImageOperations::Resize( best_bitmap, skia::ImageOperations::RESIZE_LANCZOS3, @@ -119,7 +119,7 @@ bool ConvertImageFamilyToBitmaps( // Only 32 bit ARGB bitmaps are supported. We also make sure the bitmap has // been properly initialized. SkAutoLockPixels bitmap_lock(bitmap); - if ((bitmap.config() != SkBitmap::kARGB_8888_Config) || + if ((bitmap.colorType() != kPMColor_SkColorType) || (bitmap.getPixels() == NULL)) { return false; } @@ -166,7 +166,7 @@ HICON IconUtil::CreateHICONFromSkBitmap(const SkBitmap& bitmap) { // Only 32 bit ARGB bitmaps are supported. We also try to perform as many // validations as we can on the bitmap. SkAutoLockPixels bitmap_lock(bitmap); - if ((bitmap.config() != SkBitmap::kARGB_8888_Config) || + if ((bitmap.colorType() != kPMColor_SkColorType) || (bitmap.width() <= 0) || (bitmap.height() <= 0) || (bitmap.getPixels() == NULL)) return NULL; @@ -177,13 +177,18 @@ HICON IconUtil::CreateHICONFromSkBitmap(const SkBitmap& bitmap) { // alpha mask for the DIB. BITMAPV5HEADER bitmap_header; InitializeBitmapHeader(&bitmap_header, bitmap.width(), bitmap.height()); - void* bits; - HDC hdc = ::GetDC(NULL); + + void* bits = NULL; HBITMAP dib; - dib = ::CreateDIBSection(hdc, reinterpret_cast<BITMAPINFO*>(&bitmap_header), - DIB_RGB_COLORS, &bits, NULL, 0); - DCHECK(dib); - ::ReleaseDC(NULL, hdc); + + { + base::win::ScopedGetDC hdc(NULL); + dib = ::CreateDIBSection(hdc, reinterpret_cast<BITMAPINFO*>(&bitmap_header), + DIB_RGB_COLORS, &bits, NULL, 0); + } + if (!dib || !bits) + return NULL; + memcpy(bits, bitmap.getPixels(), bitmap.width() * bitmap.height() * 4); // Icons are generally created using an AND and XOR masks where the AND @@ -290,9 +295,8 @@ scoped_ptr<SkBitmap> IconUtil::CreateSkBitmapFromIconResource(HMODULE module, DCHECK(png_data); DCHECK_EQ(png_size, large_icon_entry->dwBytesInRes); - const unsigned char* png_bytes = - reinterpret_cast<const unsigned char*>(png_data); - gfx::Image image = gfx::Image::CreateFrom1xPNGBytes(png_bytes, png_size); + gfx::Image image = gfx::Image::CreateFrom1xPNGBytes( + new base::RefCountedStaticMemory(png_data, png_size)); return scoped_ptr<SkBitmap>(new SkBitmap(image.AsBitmap())); } @@ -372,8 +376,7 @@ SkBitmap IconUtil::CreateSkBitmapFromHICONHelper(HICON icon, // Allocating memory for the SkBitmap object. We are going to create an ARGB // bitmap so we should set the configuration appropriately. SkBitmap bitmap; - bitmap.setConfig(SkBitmap::kARGB_8888_Config, s.width(), s.height()); - bitmap.allocPixels(); + bitmap.allocN32Pixels(s.width(), s.height()); bitmap.eraseARGB(0, 0, 0, 0); SkAutoLockPixels bitmap_lock(bitmap); diff --git a/chromium/ui/gfx/icon_util_unittest.cc b/chromium/ui/gfx/icon_util_unittest.cc index 641422a24ef..1e2e96297c2 100644 --- a/chromium/ui/gfx/icon_util_unittest.cc +++ b/chromium/ui/gfx/icon_util_unittest.cc @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "ui/gfx/icon_util.h" + #include "base/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/memory/scoped_ptr.h" @@ -9,11 +11,10 @@ #include "testing/gtest/include/gtest/gtest.h" #include "third_party/skia/include/core/SkBitmap.h" #include "ui/gfx/gfx_paths.h" -#include "ui/gfx/icon_util.h" +#include "ui/gfx/icon_util_unittests_resource.h" #include "ui/gfx/image/image.h" #include "ui/gfx/image/image_family.h" #include "ui/gfx/size.h" -#include "ui/test/ui_unittests_resource.h" namespace { @@ -50,9 +51,8 @@ class IconUtilTest : public testing::Test { SkBitmap CreateBlackSkBitmap(int width, int height) { SkBitmap bitmap; - bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height); - bitmap.allocPixels(); - // Setting the pixels to black. + bitmap.allocN32Pixels(width, height); + // Setting the pixels to transparent-black. memset(bitmap.getPixels(), 0, width * height * 4); return bitmap; } @@ -172,23 +172,22 @@ TEST_F(IconUtilTest, TestBitmapToIconInvalidParameters) { // Wrong bitmap format. bitmap.reset(new SkBitmap); ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL)); - bitmap->setConfig(SkBitmap::kA8_Config, kSmallIconWidth, kSmallIconHeight); + bitmap->setInfo(SkImageInfo::MakeA8(kSmallIconWidth, kSmallIconHeight)); icon = IconUtil::CreateHICONFromSkBitmap(*bitmap); EXPECT_EQ(icon, static_cast<HICON>(NULL)); // Invalid bitmap size. bitmap.reset(new SkBitmap); ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL)); - bitmap->setConfig(SkBitmap::kARGB_8888_Config, 0, 0); + bitmap->setInfo(SkImageInfo::MakeN32Premul(0, 0)); icon = IconUtil::CreateHICONFromSkBitmap(*bitmap); EXPECT_EQ(icon, static_cast<HICON>(NULL)); // Valid bitmap configuration but no pixels allocated. bitmap.reset(new SkBitmap); ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL)); - bitmap->setConfig(SkBitmap::kARGB_8888_Config, - kSmallIconWidth, - kSmallIconHeight); + bitmap->setInfo(SkImageInfo::MakeN32Premul(kSmallIconWidth, + kSmallIconHeight)); icon = IconUtil::CreateHICONFromSkBitmap(*bitmap); EXPECT_TRUE(icon == NULL); } @@ -206,10 +205,9 @@ TEST_F(IconUtilTest, TestCreateIconFileInvalidParameters) { // Wrong bitmap format. bitmap.reset(new SkBitmap); ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL)); - bitmap->setConfig(SkBitmap::kA8_Config, kSmallIconWidth, kSmallIconHeight); // Must allocate pixels or else ImageSkia will ignore the bitmap and just // return an empty image. - bitmap->allocPixels(); + bitmap->allocPixels(SkImageInfo::MakeA8(kSmallIconWidth, kSmallIconHeight)); memset(bitmap->getPixels(), 0, bitmap->width() * bitmap->height()); image_family.Add(gfx::Image::CreateFrom1xBitmap(*bitmap)); EXPECT_FALSE(IconUtil::CreateIconFileFromImageFamily(image_family, @@ -220,8 +218,7 @@ TEST_F(IconUtilTest, TestCreateIconFileInvalidParameters) { image_family.clear(); bitmap.reset(new SkBitmap); ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL)); - bitmap->setConfig(SkBitmap::kARGB_8888_Config, 0, 0); - bitmap->allocPixels(); + bitmap->allocPixels(SkImageInfo::MakeN32Premul(0, 0)); image_family.Add(gfx::Image::CreateFrom1xBitmap(*bitmap)); EXPECT_FALSE(IconUtil::CreateIconFileFromImageFamily(image_family, valid_icon_filename)); @@ -231,9 +228,8 @@ TEST_F(IconUtilTest, TestCreateIconFileInvalidParameters) { image_family.clear(); bitmap.reset(new SkBitmap); ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL)); - bitmap->setConfig(SkBitmap::kARGB_8888_Config, - kSmallIconWidth, - kSmallIconHeight); + bitmap->setInfo(SkImageInfo::MakeN32Premul(kSmallIconWidth, + kSmallIconHeight)); image_family.Add(gfx::Image::CreateFrom1xBitmap(*bitmap)); EXPECT_FALSE(IconUtil::CreateIconFileFromImageFamily(image_family, valid_icon_filename)); @@ -284,7 +280,7 @@ TEST_F(IconUtilTest, TestCreateSkBitmapFromHICON) { ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL)); EXPECT_EQ(bitmap->width(), small_icon_size.width()); EXPECT_EQ(bitmap->height(), small_icon_size.height()); - EXPECT_EQ(bitmap->config(), SkBitmap::kARGB_8888_Config); + EXPECT_EQ(bitmap->colorType(), kPMColor_SkColorType); ::DestroyIcon(small_icon); base::FilePath large_icon_filename = test_data_directory_.AppendASCII( @@ -298,7 +294,7 @@ TEST_F(IconUtilTest, TestCreateSkBitmapFromHICON) { ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL)); EXPECT_EQ(bitmap->width(), large_icon_size.width()); EXPECT_EQ(bitmap->height(), large_icon_size.height()); - EXPECT_EQ(bitmap->config(), SkBitmap::kARGB_8888_Config); + EXPECT_EQ(bitmap->colorType(), kPMColor_SkColorType); ::DestroyIcon(large_icon); } diff --git a/chromium/ui/gfx/icon_util_unittests.ico b/chromium/ui/gfx/icon_util_unittests.ico Binary files differnew file mode 100644 index 00000000000..8cd57a12299 --- /dev/null +++ b/chromium/ui/gfx/icon_util_unittests.ico diff --git a/chromium/ui/gfx/icon_util_unittests.rc b/chromium/ui/gfx/icon_util_unittests.rc new file mode 100644 index 00000000000..a44a001bc0b --- /dev/null +++ b/chromium/ui/gfx/icon_util_unittests.rc @@ -0,0 +1,36 @@ +// Microsoft Visual C++ generated resource script. +// +#include "icon_util_unittests_resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#define APSTUDIO_HIDDEN_SYMBOLS +#include "windows.h" +#undef APSTUDIO_HIDDEN_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDR_MAINFRAME ICON "icon_util_unittests.ico" + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// diff --git a/chromium/ui/gfx/icon_util_unittests_resource.h b/chromium/ui/gfx/icon_util_unittests_resource.h new file mode 100644 index 00000000000..0a50edfdd01 --- /dev/null +++ b/chromium/ui/gfx/icon_util_unittests_resource.h @@ -0,0 +1,5 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#define IDR_MAINFRAME 101 diff --git a/chromium/ui/gfx/image/OWNERS b/chromium/ui/gfx/image/OWNERS index d3273727c23..23dbb5a958f 100644 --- a/chromium/ui/gfx/image/OWNERS +++ b/chromium/ui/gfx/image/OWNERS @@ -1,4 +1,4 @@ rsesek@chromium.org # ImageSkia related classes except for _mac/_ios stuff -per-file image_skia.*=oshima@chromium.org +per-file image_skia*=oshima@chromium.org diff --git a/chromium/ui/gfx/image/cairo_cached_surface.cc b/chromium/ui/gfx/image/cairo_cached_surface.cc deleted file mode 100644 index 2de4a3210b3..00000000000 --- a/chromium/ui/gfx/image/cairo_cached_surface.cc +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/gfx/image/cairo_cached_surface.h" - -#include <gtk/gtk.h> - -#include "base/basictypes.h" -#include "base/logging.h" - -namespace gfx { - -CairoCachedSurface::CairoCachedSurface() : pixbuf_(NULL) { -} - -CairoCachedSurface::~CairoCachedSurface() { - Reset(); -} - -void CairoCachedSurface::Reset() { - for (SurfaceVector::iterator it = surface_map_.begin(); - it != surface_map_.end(); ++it) { - cairo_surface_destroy(it->second); - } - surface_map_.clear(); - - if (pixbuf_) { - g_object_unref(pixbuf_); - pixbuf_ = NULL; - } -} - -int CairoCachedSurface::Width() const { - return pixbuf_ ? gdk_pixbuf_get_width(pixbuf_) : -1; -} - -int CairoCachedSurface::Height() const { - return pixbuf_ ? gdk_pixbuf_get_height(pixbuf_) : -1; -} - -void CairoCachedSurface::UsePixbuf(GdkPixbuf* pixbuf) { - if (pixbuf) - g_object_ref(pixbuf); - - Reset(); - - pixbuf_ = pixbuf; -} - -void CairoCachedSurface::SetSource(cairo_t* cr, GtkWidget* widget, - int x, int y) const { - SetSource(cr, gtk_widget_get_display(widget), x, y); -} - -void CairoCachedSurface::SetSource(cairo_t* cr, GdkDisplay* display, - int x, int y) const { - DCHECK(pixbuf_); - DCHECK(cr); - DCHECK(display); - - cairo_surface_t* surface = GetSurfaceFor(cr, display); - cairo_set_source_surface(cr, surface, x, y); -} - -void CairoCachedSurface::MaskSource(cairo_t* cr, GtkWidget* widget, - int x, int y) const { - MaskSource(cr, gtk_widget_get_display(widget), x, y); -} - -void CairoCachedSurface::MaskSource(cairo_t* cr, GdkDisplay* display, - int x, int y) const { - DCHECK(pixbuf_); - DCHECK(cr); - DCHECK(display); - - cairo_surface_t* surface = GetSurfaceFor(cr, display); - cairo_mask_surface(cr, surface, x, y); -} - -cairo_surface_t* CairoCachedSurface::GetSurfaceFor(cairo_t* cr, - GdkDisplay* display) const { - for (SurfaceVector::const_iterator it = surface_map_.begin(); - it != surface_map_.end(); ++it) { - if (display == it->first) { - return it->second; - } - } - - // First time here since last UsePixbuf call. Generate the surface. - cairo_surface_t* target = cairo_get_target(cr); - cairo_surface_t* out = cairo_surface_create_similar( - target, - CAIRO_CONTENT_COLOR_ALPHA, - gdk_pixbuf_get_width(pixbuf_), - gdk_pixbuf_get_height(pixbuf_)); - - DCHECK(out); - - cairo_t* copy_cr = cairo_create(out); - gdk_cairo_set_source_pixbuf(copy_cr, pixbuf_, 0, 0); - cairo_paint(copy_cr); - cairo_destroy(copy_cr); - - surface_map_.push_back(std::make_pair(display, out)); - return out; -} - -} // namespace gfx diff --git a/chromium/ui/gfx/image/cairo_cached_surface.h b/chromium/ui/gfx/image/cairo_cached_surface.h deleted file mode 100644 index ceece4ba2d2..00000000000 --- a/chromium/ui/gfx/image/cairo_cached_surface.h +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_GFX_IMAGE_CAIRO_CACHED_SURFACE_H_ -#define UI_GFX_IMAGE_CAIRO_CACHED_SURFACE_H_ - -#include <vector> - -#include "ui/gfx/gfx_export.h" - -typedef struct _GdkDisplay GdkDisplay; -typedef struct _GdkPixbuf GdkPixbuf; -typedef struct _GtkWidget GtkWidget; -typedef struct _cairo cairo_t; -typedef struct _cairo_surface cairo_surface_t; - -namespace gfx { - -// A helper class that takes a GdkPixbuf* and renders it to the screen. Unlike -// gdk_cairo_set_source_pixbuf(), CairoCachedSurface assumes that the pixbuf is -// immutable after UsePixbuf() is called and can be sent to the display server -// once. From then on, that cached version is used so we don't upload the same -// image each and every time we expose. -// -// Most cached surfaces are owned by the GtkThemeService, which associates -// them with a certain XDisplay. Some users of surfaces (CustomDrawButtonBase, -// for example) own their surfaces instead since they interact with the -// ResourceBundle instead of the GtkThemeService. -class GFX_EXPORT CairoCachedSurface { - public: - CairoCachedSurface(); - ~CairoCachedSurface(); - - // Whether this CairoCachedSurface owns a GdkPixbuf. - bool valid() const { - return pixbuf_; - } - - // Delete all our data. - void Reset(); - - // The dimensions of the underlying pixbuf/surface. (or -1 if invalid.) - int Width() const; - int Height() const; - - // Sets the pixbuf that we pass to cairo. Calling UsePixbuf() only derefs the - // current pixbuf and surface (if they exist). Actually transfering data to - // the X server occurs at SetSource() time. Calling UsePixbuf() should only - // be done once as it clears cached data from the X server. - void UsePixbuf(GdkPixbuf* pixbuf); - - // Sets our pixbuf as the active surface starting at (x, y), uploading it in - // case we don't have an X backed surface cached. - void SetSource(cairo_t* cr, GtkWidget* widget, int x, int y) const; - void SetSource(cairo_t* cr, GdkDisplay* display, int x, int y) const; - - // Performs a mask operation, using this surface as the alpha channel. - void MaskSource(cairo_t* cr, GtkWidget* widget, int x, int y) const; - void MaskSource(cairo_t* cr, GdkDisplay* display, int x, int y) const; - - // Raw access to the pixbuf. May be NULL. Used for a few gdk operations - // regarding window shaping. - GdkPixbuf* pixbuf() { return pixbuf_; } - - private: - typedef std::vector<std::pair<GdkDisplay*, cairo_surface_t*> > SurfaceVector; - - // Returns a surface . Caches results so only one copy of the image data - // lives on the display server. - cairo_surface_t* GetSurfaceFor(cairo_t* cr, GdkDisplay* display) const; - - // The source pixbuf. - GdkPixbuf* pixbuf_; - - // Our list of cached surfaces. 99% of the time, this will only contain a - // single entry. At most two. We need to get this right for multiple displays - // to work correct, since each GdkDisplay is a different display server. - mutable SurfaceVector surface_map_; -}; - -} // namespace gfx - -#endif // UI_GFX_IMAGE_CAIRO_CACHED_SURFACE_H_ diff --git a/chromium/ui/gfx/image/image.cc b/chromium/ui/gfx/image/image.cc index 91b9e69b289..459880742f8 100644 --- a/chromium/ui/gfx/image/image.cc +++ b/chromium/ui/gfx/image/image.cc @@ -5,6 +5,7 @@ #include "ui/gfx/image/image.h" #include <algorithm> +#include <set> #include "base/logging.h" #include "base/memory/scoped_ptr.h" @@ -12,21 +13,14 @@ #include "third_party/skia/include/core/SkBitmap.h" #include "ui/gfx/image/image_png_rep.h" #include "ui/gfx/image/image_skia.h" +#include "ui/gfx/image/image_skia_source.h" #include "ui/gfx/size.h" #if !defined(OS_IOS) #include "ui/gfx/codec/png_codec.h" #endif -#if defined(TOOLKIT_GTK) -#include <gdk-pixbuf/gdk-pixbuf.h> -#include <gdk/gdk.h> -#include <glib-object.h> -#include "ui/gfx/canvas.h" -#include "ui/gfx/gtk_util.h" -#include "ui/gfx/image/cairo_cached_surface.h" -#include "ui/gfx/scoped_gobject.h" -#elif defined(OS_IOS) +#if defined(OS_IOS) #include "base/mac/foundation_util.h" #include "ui/gfx/image/image_skia_util_ios.h" #elif defined(OS_MACOSX) @@ -38,100 +32,25 @@ namespace gfx { namespace internal { -#if defined(TOOLKIT_GTK) -const ImageSkia ImageSkiaFromGdkPixbuf(GdkPixbuf* pixbuf) { - CHECK(pixbuf); - gfx::Canvas canvas(gfx::Size(gdk_pixbuf_get_width(pixbuf), - gdk_pixbuf_get_height(pixbuf)), - 1.0f, - false); - skia::ScopedPlatformPaint scoped_platform_paint(canvas.sk_canvas()); - cairo_t* cr = scoped_platform_paint.GetPlatformSurface(); - gdk_cairo_set_source_pixbuf(cr, pixbuf, 0, 0); - cairo_paint(cr); - return ImageSkia(canvas.ExtractImageRep()); -} - -// Returns a 16x16 red pixbuf to visually show error in decoding PNG. -// Also logs error to console. -GdkPixbuf* GetErrorPixbuf() { - LOG(ERROR) << "Unable to decode PNG."; - GdkPixbuf* pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, 16, 16); - gdk_pixbuf_fill(pixbuf, 0xff0000ff); - return pixbuf; -} - -GdkPixbuf* GdkPixbufFromPNG( - const std::vector<gfx::ImagePNGRep>& image_png_reps) { - scoped_refptr<base::RefCountedMemory> png_bytes(NULL); - for (size_t i = 0; i < image_png_reps.size(); ++i) { - if (image_png_reps[i].scale == 1.0f) - png_bytes = image_png_reps[i].raw_data; - } - - if (!png_bytes.get()) - return GetErrorPixbuf(); - - GdkPixbuf* pixbuf = NULL; - ui::ScopedGObject<GdkPixbufLoader>::Type loader(gdk_pixbuf_loader_new()); - - bool ok = gdk_pixbuf_loader_write(loader.get(), - reinterpret_cast<const guint8*>(png_bytes->front()), png_bytes->size(), - NULL); - - // Calling gdk_pixbuf_loader_close forces the data to be parsed by the - // loader. This must be done before calling gdk_pixbuf_loader_get_pixbuf. - if (ok) - ok = gdk_pixbuf_loader_close(loader.get(), NULL); - if (ok) - pixbuf = gdk_pixbuf_loader_get_pixbuf(loader.get()); - - if (pixbuf) { - // The pixbuf is owned by the scoped loader which will delete its ref when - // it goes out of scope. Add a ref so that the pixbuf still exists. - g_object_ref(pixbuf); - } else { - return GetErrorPixbuf(); - } - - return pixbuf; -} - -scoped_refptr<base::RefCountedMemory> Get1xPNGBytesFromPixbuf( - GdkPixbuf* pixbuf) { - gchar* image = NULL; - gsize image_size; - GError* error = NULL; - CHECK(gdk_pixbuf_save_to_buffer( - pixbuf, &image, &image_size, "png", &error, NULL)); - scoped_refptr<base::RefCountedBytes> png_bytes( - new base::RefCountedBytes()); - png_bytes->data().assign(image, image + image_size); - g_free(image); - return png_bytes; -} - -#endif // defined(TOOLKIT_GTK) - #if defined(OS_IOS) scoped_refptr<base::RefCountedMemory> Get1xPNGBytesFromUIImage( UIImage* uiimage); // Caller takes ownership of the returned UIImage. UIImage* CreateUIImageFromPNG( - const std::vector<gfx::ImagePNGRep>& image_png_reps); + const std::vector<ImagePNGRep>& image_png_reps); gfx::Size UIImageSize(UIImage* image); #elif defined(OS_MACOSX) scoped_refptr<base::RefCountedMemory> Get1xPNGBytesFromNSImage( NSImage* nsimage); // Caller takes ownership of the returned NSImage. -NSImage* NSImageFromPNG(const std::vector<gfx::ImagePNGRep>& image_png_reps, +NSImage* NSImageFromPNG(const std::vector<ImagePNGRep>& image_png_reps, CGColorSpaceRef color_space); gfx::Size NSImageSize(NSImage* image); #endif // defined(OS_MACOSX) #if defined(OS_IOS) ImageSkia* ImageSkiaFromPNG( - const std::vector<gfx::ImagePNGRep>& image_png_reps); + const std::vector<ImagePNGRep>& image_png_reps); scoped_refptr<base::RefCountedMemory> Get1xPNGBytesFromImageSkia( const ImageSkia* skia); #else @@ -141,32 +60,92 @@ ImageSkia* GetErrorImageSkia() { SkBitmap bitmap; bitmap.setConfig(SkBitmap::kARGB_8888_Config, 16, 16); bitmap.allocPixels(); - bitmap.eraseRGB(0xff, 0, 0); - return new gfx::ImageSkia(gfx::ImageSkiaRep(bitmap, 1.0f)); + bitmap.eraseARGB(0xff, 0xff, 0, 0); + return new ImageSkia(ImageSkiaRep(bitmap, 1.0f)); } +class PNGImageSource : public ImageSkiaSource { + public: + PNGImageSource() {} + virtual ~PNGImageSource() {} + + virtual ImageSkiaRep GetImageForScale(float scale) OVERRIDE { + if (image_skia_reps_.empty()) + return ImageSkiaRep(); + + const ImageSkiaRep* rep = NULL; + // gfx::ImageSkia passes one of the resource scale factors. The source + // should return: + // 1) The ImageSkiaRep with the highest scale if all available + // scales are smaller than |scale|. + // 2) The ImageSkiaRep with the smallest one that is larger than |scale|. + for (ImageSkiaRepSet::const_iterator iter = image_skia_reps_.begin(); + iter != image_skia_reps_.end(); ++iter) { + if ((*iter).scale() == scale) + return (*iter); + if (!rep || rep->scale() < (*iter).scale()) + rep = &(*iter); + if (rep->scale() >= scale) + break; + } + return rep ? *rep : ImageSkiaRep(); + } + + const gfx::Size size() const { + return size_; + } + + bool AddPNGData(const ImagePNGRep& png_rep) { + const gfx::ImageSkiaRep rep = ToImageSkiaRep(png_rep); + if (rep.is_null()) + return false; + if (size_.IsEmpty()) + size_ = gfx::Size(rep.GetWidth(), rep.GetHeight()); + image_skia_reps_.insert(rep); + return true; + } + + static ImageSkiaRep ToImageSkiaRep(const ImagePNGRep& png_rep) { + scoped_refptr<base::RefCountedMemory> raw_data = png_rep.raw_data; + CHECK(raw_data.get()); + SkBitmap bitmap; + if (!PNGCodec::Decode(raw_data->front(), raw_data->size(), + &bitmap)) { + LOG(ERROR) << "Unable to decode PNG for " << png_rep.scale << "."; + return ImageSkiaRep(); + } + return ImageSkiaRep(bitmap, png_rep.scale); + } + + private: + struct Compare { + bool operator()(const ImageSkiaRep& rep1, const ImageSkiaRep& rep2) { + return rep1.scale() < rep2.scale(); + } + }; + + typedef std::set<ImageSkiaRep, Compare> ImageSkiaRepSet; + ImageSkiaRepSet image_skia_reps_; + gfx::Size size_; + + DISALLOW_COPY_AND_ASSIGN(PNGImageSource); +}; + ImageSkia* ImageSkiaFromPNG( - const std::vector<gfx::ImagePNGRep>& image_png_reps) { + const std::vector<ImagePNGRep>& image_png_reps) { if (image_png_reps.empty()) return GetErrorImageSkia(); + scoped_ptr<PNGImageSource> image_source(new PNGImageSource); - scoped_ptr<gfx::ImageSkia> image_skia(new ImageSkia()); for (size_t i = 0; i < image_png_reps.size(); ++i) { - scoped_refptr<base::RefCountedMemory> raw_data = - image_png_reps[i].raw_data; - CHECK(raw_data.get()); - SkBitmap bitmap; - if (!gfx::PNGCodec::Decode(raw_data->front(), raw_data->size(), - &bitmap)) { - LOG(ERROR) << "Unable to decode PNG for " - << image_png_reps[i].scale - << "."; + if (!image_source->AddPNGData(image_png_reps[i])) return GetErrorImageSkia(); - } - image_skia->AddRepresentation(gfx::ImageSkiaRep( - bitmap, image_png_reps[i].scale)); } - return image_skia.release(); + const gfx::Size& size = image_source->size(); + DCHECK(!size.IsEmpty()); + if (size.IsEmpty()) + return GetErrorImageSkia(); + return new ImageSkia(image_source.release(), size); } scoped_refptr<base::RefCountedMemory> Get1xPNGBytesFromImageSkia( @@ -175,7 +154,7 @@ scoped_refptr<base::RefCountedMemory> Get1xPNGBytesFromImageSkia( scoped_refptr<base::RefCountedBytes> png_bytes(new base::RefCountedBytes()); if (image_skia_rep.scale() != 1.0f || - !gfx::PNGCodec::EncodeBGRASkBitmap(image_skia_rep.sk_bitmap(), false, + !PNGCodec::EncodeBGRASkBitmap(image_skia_rep.sk_bitmap(), false, &png_bytes->data())) { return NULL; } @@ -185,8 +164,6 @@ scoped_refptr<base::RefCountedMemory> Get1xPNGBytesFromImageSkia( class ImageRepPNG; class ImageRepSkia; -class ImageRepGdk; -class ImageRepCairo; class ImageRepCocoa; class ImageRepCocoaTouch; @@ -213,18 +190,6 @@ class ImageRep { return reinterpret_cast<ImageRepSkia*>(this); } -#if defined(TOOLKIT_GTK) - ImageRepGdk* AsImageRepGdk() { - CHECK_EQ(type_, Image::kImageRepGdk); - return reinterpret_cast<ImageRepGdk*>(this); - } - - ImageRepCairo* AsImageRepCairo() { - CHECK_EQ(type_, Image::kImageRepCairo); - return reinterpret_cast<ImageRepCairo*>(this); - } -#endif - #if defined(OS_IOS) ImageRepCocoaTouch* AsImageRepCocoaTouch() { CHECK_EQ(type_, Image::kImageRepCocoaTouch); @@ -326,77 +291,6 @@ class ImageRepSkia : public ImageRep { DISALLOW_COPY_AND_ASSIGN(ImageRepSkia); }; -#if defined(TOOLKIT_GTK) -class ImageRepGdk : public ImageRep { - public: - explicit ImageRepGdk(GdkPixbuf* pixbuf) - : ImageRep(Image::kImageRepGdk), - pixbuf_(pixbuf) { - CHECK(pixbuf); - } - - virtual ~ImageRepGdk() { - if (pixbuf_) { - g_object_unref(pixbuf_); - pixbuf_ = NULL; - } - } - - virtual int Width() const OVERRIDE { - return gdk_pixbuf_get_width(pixbuf_); - } - - virtual int Height() const OVERRIDE { - return gdk_pixbuf_get_height(pixbuf_); - } - - virtual gfx::Size Size() const OVERRIDE { - return gfx::Size(Width(), Height()); - } - - GdkPixbuf* pixbuf() const { return pixbuf_; } - - private: - GdkPixbuf* pixbuf_; - - DISALLOW_COPY_AND_ASSIGN(ImageRepGdk); -}; - -// Represents data that lives on the display server instead of in the client. -class ImageRepCairo : public ImageRep { - public: - explicit ImageRepCairo(GdkPixbuf* pixbuf) - : ImageRep(Image::kImageRepCairo), - cairo_cache_(new CairoCachedSurface) { - CHECK(pixbuf); - cairo_cache_->UsePixbuf(pixbuf); - } - - virtual ~ImageRepCairo() { - delete cairo_cache_; - } - - virtual int Width() const OVERRIDE { - return cairo_cache_->Width(); - } - - virtual int Height() const OVERRIDE { - return cairo_cache_->Height(); - } - - virtual gfx::Size Size() const OVERRIDE { - return gfx::Size(Width(), Height()); - } - - CairoCachedSurface* surface() const { return cairo_cache_; } - - private: - CairoCachedSurface* cairo_cache_; - - DISALLOW_COPY_AND_ASSIGN(ImageRepCairo); -}; -#endif // defined(TOOLKIT_GTK) - #if defined(OS_IOS) class ImageRepCocoaTouch : public ImageRep { public: @@ -470,7 +364,7 @@ class ImageRepCocoa : public ImageRep { // ImageReps. This way, the Image can be cheaply copied. class ImageStorage : public base::RefCounted<ImageStorage> { public: - ImageStorage(gfx::Image::RepresentationType default_type) + ImageStorage(Image::RepresentationType default_type) : default_representation_type_(default_type), #if defined(OS_MACOSX) && !defined(OS_IOS) default_representation_color_space_( @@ -479,10 +373,10 @@ class ImageStorage : public base::RefCounted<ImageStorage> { representations_deleter_(&representations_) { } - gfx::Image::RepresentationType default_representation_type() { + Image::RepresentationType default_representation_type() { return default_representation_type_; } - gfx::Image::RepresentationMap& representations() { return representations_; } + Image::RepresentationMap& representations() { return representations_; } #if defined(OS_MACOSX) && !defined(OS_IOS) void set_default_representation_color_space(CGColorSpaceRef color_space) { @@ -500,7 +394,7 @@ class ImageStorage : public base::RefCounted<ImageStorage> { // The type of image that was passed to the constructor. This key will always // exist in the |representations_| map. - gfx::Image::RepresentationType default_representation_type_; + Image::RepresentationType default_representation_type_; #if defined(OS_MACOSX) && !defined(OS_IOS) // The default representation's colorspace. This is used for converting to @@ -512,7 +406,7 @@ class ImageStorage : public base::RefCounted<ImageStorage> { // All the representations of an Image. Size will always be at least one, with // more for any converted representations. - gfx::Image::RepresentationMap representations_; + Image::RepresentationMap representations_; STLValueDeleter<Image::RepresentationMap> representations_deleter_; @@ -550,16 +444,6 @@ Image::Image(const ImageSkia& image) { } } -#if defined(TOOLKIT_GTK) -Image::Image(GdkPixbuf* pixbuf) { - if (pixbuf) { - storage_ = new internal::ImageStorage(Image::kImageRepGdk); - internal::ImageRepGdk* rep = new internal::ImageRepGdk(pixbuf); - AddRepresentation(rep); - } -} -#endif - #if defined(OS_IOS) Image::Image(UIImage* image) : storage_(new internal::ImageStorage(Image::kImageRepCocoaTouch)) { @@ -591,20 +475,29 @@ Image::~Image() { // static Image Image::CreateFrom1xBitmap(const SkBitmap& bitmap) { - return gfx::Image(ImageSkia::CreateFrom1xBitmap(bitmap)); + return Image(ImageSkia::CreateFrom1xBitmap(bitmap)); } // static Image Image::CreateFrom1xPNGBytes(const unsigned char* input, size_t input_size) { if (input_size == 0u) - return gfx::Image(); + return Image(); scoped_refptr<base::RefCountedBytes> raw_data(new base::RefCountedBytes()); raw_data->data().assign(input, input + input_size); - std::vector<gfx::ImagePNGRep> image_reps; - image_reps.push_back(ImagePNGRep(raw_data, 1.0f)); - return gfx::Image(image_reps); + + return CreateFrom1xPNGBytes(raw_data); +} + +Image Image::CreateFrom1xPNGBytes( + const scoped_refptr<base::RefCountedMemory>& input) { + if (!input.get() || input->size() == 0u) + return Image(); + + std::vector<ImagePNGRep> image_reps; + image_reps.push_back(ImagePNGRep(input, 1.0f)); + return Image(image_reps); } const SkBitmap* Image::ToSkBitmap() const { @@ -623,15 +516,7 @@ const ImageSkia* Image::ToImageSkia() const { internal::ImageSkiaFromPNG(png_rep->image_reps())); break; } -#if defined(TOOLKIT_GTK) - case kImageRepGdk: { - internal::ImageRepGdk* native_rep = - GetRepresentation(kImageRepGdk, true)->AsImageRepGdk(); - rep = new internal::ImageRepSkia(new ImageSkia( - internal::ImageSkiaFromGdkPixbuf(native_rep->pixbuf()))); - break; - } -#elif defined(OS_IOS) +#if defined(OS_IOS) case kImageRepCocoaTouch: { internal::ImageRepCocoaTouch* native_rep = GetRepresentation(kImageRepCocoaTouch, true) @@ -658,47 +543,6 @@ const ImageSkia* Image::ToImageSkia() const { return rep->AsImageRepSkia()->image(); } -#if defined(TOOLKIT_GTK) -GdkPixbuf* Image::ToGdkPixbuf() const { - internal::ImageRep* rep = GetRepresentation(kImageRepGdk, false); - if (!rep) { - switch (DefaultRepresentationType()) { - case kImageRepPNG: { - internal::ImageRepPNG* png_rep = - GetRepresentation(kImageRepPNG, true)->AsImageRepPNG(); - rep = new internal::ImageRepGdk(internal::GdkPixbufFromPNG( - png_rep->image_reps())); - break; - } - case kImageRepSkia: { - internal::ImageRepSkia* skia_rep = - GetRepresentation(kImageRepSkia, true)->AsImageRepSkia(); - rep = new internal::ImageRepGdk(gfx::GdkPixbufFromSkBitmap( - *skia_rep->image()->bitmap())); - break; - } - default: - NOTREACHED(); - } - CHECK(rep); - AddRepresentation(rep); - } - return rep->AsImageRepGdk()->pixbuf(); -} - -CairoCachedSurface* const Image::ToCairo() const { - internal::ImageRep* rep = GetRepresentation(kImageRepCairo, false); - if (!rep) { - // Handle any-to-Cairo conversion. This may create and cache an intermediate - // pixbuf before sending the data to the display server. - rep = new internal::ImageRepCairo(ToGdkPixbuf()); - CHECK(rep); - AddRepresentation(rep); - } - return rep->AsImageRepCairo()->surface(); -} -#endif - #if defined(OS_IOS) UIImage* Image::ToUIImage() const { internal::ImageRep* rep = GetRepresentation(kImageRepCocoaTouch, false); @@ -768,7 +612,7 @@ scoped_refptr<base::RefCountedMemory> Image::As1xPNGBytes() const { internal::ImageRep* rep = GetRepresentation(kImageRepPNG, false); if (rep) { - const std::vector<gfx::ImagePNGRep>& image_png_reps = + const std::vector<ImagePNGRep>& image_png_reps = rep->AsImageRepPNG()->image_reps(); for (size_t i = 0; i < image_png_reps.size(); ++i) { if (image_png_reps[i].scale == 1.0f) @@ -779,14 +623,7 @@ scoped_refptr<base::RefCountedMemory> Image::As1xPNGBytes() const { scoped_refptr<base::RefCountedMemory> png_bytes(NULL); switch (DefaultRepresentationType()) { -#if defined(TOOLKIT_GTK) - case kImageRepGdk: { - internal::ImageRepGdk* gdk_rep = - GetRepresentation(kImageRepGdk, true)->AsImageRepGdk(); - png_bytes = internal::Get1xPNGBytesFromPixbuf(gdk_rep->pixbuf()); - break; - } -#elif defined(OS_IOS) +#if defined(OS_IOS) case kImageRepCocoaTouch: { internal::ImageRepCocoaTouch* cocoa_touch_rep = GetRepresentation(kImageRepCocoaTouch, true) @@ -826,7 +663,7 @@ scoped_refptr<base::RefCountedMemory> Image::As1xPNGBytes() const { // final type eg (converting from ImageRepSkia to ImageRepPNG to get an // ImageRepCocoa). std::vector<ImagePNGRep> image_png_reps; - image_png_reps.push_back(gfx::ImagePNGRep(png_bytes, 1.0f)); + image_png_reps.push_back(ImagePNGRep(png_bytes, 1.0f)); rep = new internal::ImageRepPNG(image_png_reps); AddRepresentation(rep); return png_bytes; @@ -861,14 +698,6 @@ SkBitmap* Image::CopySkBitmap() const { return new SkBitmap(*ToSkBitmap()); } -#if defined(TOOLKIT_GTK) -GdkPixbuf* Image::CopyGdkPixbuf() const { - GdkPixbuf* pixbuf = ToGdkPixbuf(); - g_object_ref(pixbuf); - return pixbuf; -} -#endif - #if defined(OS_IOS) UIImage* Image::CopyUIImage() const { UIImage* image = ToUIImage(); @@ -916,7 +745,7 @@ gfx::Size Image::Size() const { return GetRepresentation(DefaultRepresentationType(), true)->Size(); } -void Image::SwapRepresentations(gfx::Image* other) { +void Image::SwapRepresentations(Image* other) { storage_.swap(other->storage_); } @@ -929,11 +758,7 @@ void Image::SetSourceColorSpace(CGColorSpaceRef color_space) { Image::RepresentationType Image::DefaultRepresentationType() const { CHECK(storage_.get()); - RepresentationType default_type = storage_->default_representation_type(); - // The conversions above assume that the default representation type is never - // kImageRepCairo. - DCHECK_NE(default_type, kImageRepCairo); - return default_type; + return storage_->default_representation_type(); } internal::ImageRep* Image::GetRepresentation( diff --git a/chromium/ui/gfx/image/image.h b/chromium/ui/gfx/image/image.h index d4093b74063..19ca93dada9 100644 --- a/chromium/ui/gfx/image/image.h +++ b/chromium/ui/gfx/image/image.h @@ -44,10 +44,6 @@ struct ImagePNGRep; class ImageSkia; class Size; -#if defined(TOOLKIT_GTK) -class CairoCachedSurface; -#endif - namespace internal { class ImageRep; class ImageStorage; @@ -56,10 +52,8 @@ class ImageStorage; class GFX_EXPORT Image { public: enum RepresentationType { - kImageRepGdk, kImageRepCocoa, kImageRepCocoaTouch, - kImageRepCairo, kImageRepSkia, kImageRepPNG, }; @@ -77,10 +71,7 @@ class GFX_EXPORT Image { // representation. explicit Image(const ImageSkia& image); -#if defined(TOOLKIT_GTK) - // Does not increase |pixbuf|'s reference count; expects to take ownership. - explicit Image(GdkPixbuf* pixbuf); -#elif defined(OS_IOS) +#if defined(OS_IOS) // Does not retain |image|; expects to take ownership. explicit Image(UIImage* image); #elif defined(OS_MACOSX) @@ -113,15 +104,16 @@ class GFX_EXPORT Image { static Image CreateFrom1xPNGBytes(const unsigned char* input, size_t input_size); + // Creates an image from the PNG encoded input. + static Image CreateFrom1xPNGBytes( + const scoped_refptr<base::RefCountedMemory>& input); + // Converts the Image to the desired representation and stores it internally. // The returned result is a weak pointer owned by and scoped to the life of // the Image. Must only be called if IsEmpty() is false. const SkBitmap* ToSkBitmap() const; const ImageSkia* ToImageSkia() const; -#if defined(TOOLKIT_GTK) - GdkPixbuf* ToGdkPixbuf() const; - CairoCachedSurface* const ToCairo() const; -#elif defined(OS_IOS) +#if defined(OS_IOS) UIImage* ToUIImage() const; #elif defined(OS_MACOSX) NSImage* ToNSImage() const; @@ -154,9 +146,7 @@ class GFX_EXPORT Image { scoped_refptr<base::RefCountedMemory> Copy1xPNGBytes() const; ImageSkia* CopyImageSkia() const; SkBitmap* CopySkBitmap() const; -#if defined(TOOLKIT_GTK) - GdkPixbuf* CopyGdkPixbuf() const; -#elif defined(OS_IOS) +#if defined(OS_IOS) UIImage* CopyUIImage() const; #elif defined(OS_MACOSX) NSImage* CopyNSImage() const; diff --git a/chromium/ui/gfx/image/image_skia.cc b/chromium/ui/gfx/image/image_skia.cc index a283117db24..21d08de3003 100644 --- a/chromium/ui/gfx/image/image_skia.cc +++ b/chromium/ui/gfx/image/image_skia.cc @@ -8,14 +8,17 @@ #include <cmath> #include <limits> +#include "base/command_line.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/threading/non_thread_safe.h" +#include "ui/gfx/geometry/size_conversions.h" #include "ui/gfx/image/image_skia_operations.h" #include "ui/gfx/image/image_skia_source.h" #include "ui/gfx/rect.h" #include "ui/gfx/size.h" #include "ui/gfx/skia_util.h" +#include "ui/gfx/switches.h" namespace gfx { namespace { @@ -27,6 +30,13 @@ gfx::ImageSkiaRep& NullImageRep() { } std::vector<float>* g_supported_scales = NULL; + +// The difference to fall back to the smaller scale factor rather than the +// larger one. For example, assume 1.25 is requested but only 1.0 and 2.0 are +// supported. In that case, not fall back to 2.0 but 1.0, and then expand +// the image to 1.25. +const float kFallbackToSmallerScaleDiff = 0.25f; + } // namespace namespace internal { @@ -102,12 +112,36 @@ class ImageSkiaStorage : public base::RefCountedThreadSafe<ImageSkiaStorage>, return (read_only_ && !source_.get()) || CalledOnValidThread(); } + // Add a new representation. This checks if the scale of the added image + // is not 1.0f, and mark the existing rep as scaled to make + // the image high DPI aware. + void AddRepresentation(const ImageSkiaRep& image) { + if (image.scale() != 1.0f) { + for (ImageSkia::ImageSkiaReps::iterator it = image_reps_.begin(); + it < image_reps_.end(); + ++it) { + if (it->unscaled()) { + DCHECK_EQ(1.0f, it->scale()); + it->SetScaled(); + break; + } + } + } + image_reps_.push_back(image); + } + // Returns the iterator of the image rep whose density best matches // |scale|. If the image for the |scale| doesn't exist in the storage and // |storage| is set, it fetches new image by calling - // |ImageSkiaSource::GetImageForScale|. If the source returns the image with - // different scale (if the image doesn't exist in resource, for example), it - // will fallback to closest image rep. + // |ImageSkiaSource::GetImageForScale|. There are two modes to deal with + // arbitrary scale factors. + // 1: Invoke GetImageForScale with requested scale and if the source + // returns the image with different scale (if the image doesn't exist in + // resource, for example), it will fallback to closest image rep. + // 2: Invoke GetImageForScale with the closest known scale to the requested + // one and rescale the image. + // Right now only Windows uses 2 and other platforms use 1 by default. + // TODO(mukai, oshima): abandon 1 code path and use 2 for every platforms. std::vector<ImageSkiaRep>::iterator FindRepresentation( float scale, bool fetch_new_image) const { ImageSkiaStorage* non_const = const_cast<ImageSkiaStorage*>(this); @@ -139,7 +173,47 @@ class ImageSkiaStorage : public base::RefCountedThreadSafe<ImageSkiaStorage>, DCHECK(CalledOnValidThread()) << "An ImageSkia with the source must be accessed by the same thread."; - ImageSkiaRep image = source_->GetImageForScale(scale); + ImageSkiaRep image; + float resource_scale = scale; + if (ImageSkia::IsDSFScalingInImageSkiaEnabled() && g_supported_scales) { + if (g_supported_scales->back() <= scale) { + resource_scale = g_supported_scales->back(); + } else { + for (size_t i = 0; i < g_supported_scales->size(); ++i) { + if ((*g_supported_scales)[i] + kFallbackToSmallerScaleDiff >= + scale) { + resource_scale = (*g_supported_scales)[i]; + break; + } + } + } + } + if (ImageSkia::IsDSFScalingInImageSkiaEnabled() && + scale != resource_scale) { + std::vector<ImageSkiaRep>::iterator iter = FindRepresentation( + resource_scale, fetch_new_image); + + DCHECK(iter != image_reps_.end()); + + if (!iter->unscaled()) { + SkBitmap scaled_image; + gfx::Size unscaled_size(iter->pixel_width(), iter->pixel_height()); + gfx::Size scaled_size = ToCeiledSize( + gfx::ScaleSize(unscaled_size, scale / iter->scale())); + + image = ImageSkiaRep(skia::ImageOperations::Resize( + iter->sk_bitmap(), + skia::ImageOperations::RESIZE_LANCZOS3, + scaled_size.width(), + scaled_size.height()), scale); + DCHECK_EQ(image.pixel_width(), scaled_size.width()); + DCHECK_EQ(image.pixel_height(), scaled_size.height()); + } else { + image = *iter; + } + } else { + image = source_->GetImageForScale(scale); + } // If the source returned the new image, store it. if (!image.is_null() && @@ -240,7 +314,13 @@ float ImageSkia::GetMaxSupportedScale() { // static ImageSkia ImageSkia::CreateFrom1xBitmap(const SkBitmap& bitmap) { - return ImageSkia(ImageSkiaRep(bitmap, 1.0f)); + return ImageSkia(ImageSkiaRep(bitmap, 0.0f)); +} + +bool ImageSkia::IsDSFScalingInImageSkiaEnabled() { + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); + return !command_line->HasSwitch( + switches::kDisableArbitraryScaleFactorInImageSkia); } scoped_ptr<ImageSkia> ImageSkia::DeepCopy() const { @@ -278,7 +358,9 @@ void ImageSkia::AddRepresentation(const ImageSkiaRep& image_rep) { Init(image_rep); } else { CHECK(CanModify()); - storage_->image_reps().push_back(image_rep); + // If someone is adding ImageSkia explicitly, check if we should + // make the image high DPI aware. + storage_->AddRepresentation(image_rep); } } diff --git a/chromium/ui/gfx/image/image_skia.h b/chromium/ui/gfx/image/image_skia.h index fa5d070ad9b..8765457b432 100644 --- a/chromium/ui/gfx/image/image_skia.h +++ b/chromium/ui/gfx/image/image_skia.h @@ -78,6 +78,10 @@ class GFX_EXPORT ImageSkia { // density display. static ImageSkia CreateFrom1xBitmap(const SkBitmap& bitmap); + // Returns true when ImageSkia looks up the resource pack with the closest + // scale factor and rescale the fetched image. + static bool IsDSFScalingInImageSkiaEnabled(); + // Returns a deep copy of this ImageSkia which has its own storage with // the ImageSkiaRep instances that this ImageSkia currently has. // This can be safely passed to and manipulated by another thread. diff --git a/chromium/ui/gfx/image/image_skia_rep.cc b/chromium/ui/gfx/image/image_skia_rep.cc index f8f3acb1946..f31db600465 100644 --- a/chromium/ui/gfx/image/image_skia_rep.cc +++ b/chromium/ui/gfx/image/image_skia_rep.cc @@ -4,9 +4,11 @@ #include "ui/gfx/image/image_skia_rep.h" +#include "base/logging.h" + namespace gfx { -ImageSkiaRep::ImageSkiaRep() : scale_(1.0f) { +ImageSkiaRep::ImageSkiaRep() : scale_(0.0f) { } ImageSkiaRep::~ImageSkiaRep() { @@ -14,9 +16,10 @@ ImageSkiaRep::~ImageSkiaRep() { ImageSkiaRep::ImageSkiaRep(const gfx::Size& size, float scale) : scale_(scale) { bitmap_.setConfig(SkBitmap::kARGB_8888_Config, - static_cast<int>(size.width() * scale), - static_cast<int>(size.height() * scale)); + static_cast<int>(size.width() * this->scale()), + static_cast<int>(size.height() * this->scale())); bitmap_.allocPixels(); + bitmap_.eraseColor(SK_ColorRED); } ImageSkiaRep::ImageSkiaRep(const SkBitmap& src, float scale) @@ -25,11 +28,17 @@ ImageSkiaRep::ImageSkiaRep(const SkBitmap& src, float scale) } int ImageSkiaRep::GetWidth() const { - return static_cast<int>(bitmap_.width() / scale_); + return static_cast<int>(bitmap_.width() / scale()); } int ImageSkiaRep::GetHeight() const { - return static_cast<int>(bitmap_.height() / scale_); + return static_cast<int>(bitmap_.height() / scale()); +} + +void ImageSkiaRep::SetScaled() { + DCHECK_EQ(0.0f, scale_); + if (scale_ == 0.0f) + scale_ = 1.0f; } } // namespace gfx diff --git a/chromium/ui/gfx/image/image_skia_rep.h b/chromium/ui/gfx/image/image_skia_rep.h index 7a1e83709ab..47f6713acf5 100644 --- a/chromium/ui/gfx/image/image_skia_rep.h +++ b/chromium/ui/gfx/image/image_skia_rep.h @@ -12,14 +12,20 @@ namespace gfx { // An ImageSkiaRep represents a bitmap and the scale factor it is intended for. +// 0.0f scale is used to indicate that this ImageSkiaRep is used for unscaled +// image (the image that only returns 1.0f scale image). class GFX_EXPORT ImageSkiaRep { public: // Create null bitmap. ImageSkiaRep(); ~ImageSkiaRep(); + // Note: This is for testing purpose only. // Creates a bitmap with kARGB_8888_Config config with given |size| in DIP. - // This allocates pixels in the bitmap. + // This allocates pixels in the bitmap. It is guaranteed that the data in the + // bitmap are initialized but the actual values are undefined. + // Specifying 0 scale means the image is for unscaled image. (unscaled() + // returns truen, and scale() returns 1.0f;) ImageSkiaRep(const gfx::Size& size, float scale); // Creates a bitmap with given scale. @@ -41,7 +47,12 @@ class GFX_EXPORT ImageSkiaRep { } // Retrieves the scale that the bitmap will be painted at. - float scale() const { return scale_; } + float scale() const { return unscaled() ? 1.0f : scale_; } + + bool unscaled() const { return scale_ == 0.0f; } + + // Mark the image to be used as scaled image. + void SetScaled(); // Returns backing bitmap. const SkBitmap& sk_bitmap() const { return bitmap_; } @@ -51,6 +62,7 @@ class GFX_EXPORT ImageSkiaRep { SkBitmap& mutable_sk_bitmap() { return bitmap_; } SkBitmap bitmap_; + float scale_; }; diff --git a/chromium/ui/gfx/image/image_skia_unittest.cc b/chromium/ui/gfx/image/image_skia_unittest.cc index 0047822122b..e3e93c73b78 100644 --- a/chromium/ui/gfx/image/image_skia_unittest.cc +++ b/chromium/ui/gfx/image/image_skia_unittest.cc @@ -4,6 +4,7 @@ #include "ui/gfx/image/image_skia.h" +#include "base/command_line.h" #include "base/logging.h" #include "base/threading/simple_thread.h" #include "testing/gtest/include/gtest/gtest.h" @@ -11,6 +12,7 @@ #include "ui/gfx/image/image_skia_rep.h" #include "ui/gfx/image/image_skia_source.h" #include "ui/gfx/size.h" +#include "ui/gfx/switches.h" // Duplicated from base/threading/non_thread_safe.h so that we can be // good citizens there and undef the macro. @@ -43,17 +45,27 @@ class FixedSource : public ImageSkiaSource { class DynamicSource : public ImageSkiaSource { public: - DynamicSource(const gfx::Size& size) : size_(size) {} + DynamicSource(const gfx::Size& size) + : size_(size), + last_requested_scale_(0.0f) {} virtual ~DynamicSource() { } virtual ImageSkiaRep GetImageForScale(float scale) OVERRIDE { + last_requested_scale_ = scale; return gfx::ImageSkiaRep(size_, scale); } + float GetLastRequestedScaleAndReset() { + float result = last_requested_scale_; + last_requested_scale_ = 0.0f; + return result; + } + private: gfx::Size size_; + float last_requested_scale_; DISALLOW_COPY_AND_ASSIGN(DynamicSource); }; @@ -113,7 +125,28 @@ class TestOnThread : public base::SimpleThread { } // namespace test -TEST(ImageSkiaTest, FixedSource) { +class ImageSkiaTest : public testing::Test { + public: + ImageSkiaTest() { + // In the test, we assume that we support 1.0f and 2.0f DSFs. + old_scales_ = ImageSkia::GetSupportedScales(); + + // Sets the list of scale factors supported by resource bundle. + std::vector<float> supported_scales; + supported_scales.push_back(1.0f); + supported_scales.push_back(2.0f); + ImageSkia::SetSupportedScales(supported_scales); + } + virtual ~ImageSkiaTest() { + ImageSkia::SetSupportedScales(old_scales_); + } + + private: + std::vector<float> old_scales_; + DISALLOW_COPY_AND_ASSIGN(ImageSkiaTest); +}; + +TEST_F(ImageSkiaTest, FixedSource) { ImageSkiaRep image(Size(100, 200), 1.0f); ImageSkia image_skia(new FixedSource(image), Size(100, 200)); EXPECT_EQ(0U, image_skia.image_reps().size()); @@ -140,7 +173,7 @@ TEST(ImageSkiaTest, FixedSource) { EXPECT_EQ(1U, image_skia.image_reps().size()); } -TEST(ImageSkiaTest, DynamicSource) { +TEST_F(ImageSkiaTest, DynamicSource) { ImageSkia image_skia(new DynamicSource(Size(100, 200)), Size(100, 200)); EXPECT_EQ(0U, image_skia.image_reps().size()); const ImageSkiaRep& result_100p = image_skia.GetRepresentation(1.0f); @@ -169,7 +202,7 @@ TEST(ImageSkiaTest, DynamicSource) { // Tests that image_reps returns all of the representations in the // image when there are multiple representations for a scale factor. // This currently is the case with ImageLoader::LoadImages. -TEST(ImageSkiaTest, ManyRepsPerScaleFactor) { +TEST_F(ImageSkiaTest, ManyRepsPerScaleFactor) { const int kSmallIcon1x = 16; const int kSmallIcon2x = 32; const int kLargeIcon1x = 32; @@ -204,14 +237,14 @@ TEST(ImageSkiaTest, ManyRepsPerScaleFactor) { EXPECT_EQ(1, num_2x); } -TEST(ImageSkiaTest, GetBitmap) { +TEST_F(ImageSkiaTest, GetBitmap) { ImageSkia image_skia(new DynamicSource(Size(100, 200)), Size(100, 200)); const SkBitmap* bitmap = image_skia.bitmap(); EXPECT_NE(static_cast<SkBitmap*>(NULL), bitmap); EXPECT_FALSE(bitmap->isNull()); } -TEST(ImageSkiaTest, GetBitmapFromEmpty) { +TEST_F(ImageSkiaTest, GetBitmapFromEmpty) { // Create an image with 1 representation and remove it so the ImageSkiaStorage // is left with no representations. ImageSkia empty_image(ImageSkiaRep(Size(100, 200), 1.0f)); @@ -226,7 +259,7 @@ TEST(ImageSkiaTest, GetBitmapFromEmpty) { EXPECT_TRUE(bitmap->empty()); } -TEST(ImageSkiaTest, BackedBySameObjectAs) { +TEST_F(ImageSkiaTest, BackedBySameObjectAs) { // Null images should all be backed by the same object (NULL). ImageSkia image; ImageSkia unrelated; @@ -245,7 +278,7 @@ TEST(ImageSkiaTest, BackedBySameObjectAs) { } #if ENABLE_NON_THREAD_SAFE -TEST(ImageSkiaTest, EmptyOnThreadTest) { +TEST_F(ImageSkiaTest, EmptyOnThreadTest) { ImageSkia empty; test::TestOnThread empty_on_thread(&empty); empty_on_thread.Start(); @@ -254,7 +287,7 @@ TEST(ImageSkiaTest, EmptyOnThreadTest) { EXPECT_TRUE(empty_on_thread.can_modify()); } -TEST(ImageSkiaTest, StaticOnThreadTest) { +TEST_F(ImageSkiaTest, StaticOnThreadTest) { ImageSkia image(ImageSkiaRep(Size(100, 200), 1.0f)); EXPECT_FALSE(image.IsThreadSafe()); @@ -323,7 +356,7 @@ TEST(ImageSkiaTest, StaticOnThreadTest) { EXPECT_FALSE(image.CanModify()); } -TEST(ImageSkiaTest, SourceOnThreadTest) { +TEST_F(ImageSkiaTest, SourceOnThreadTest) { ImageSkia image(new DynamicSource(Size(100, 200)), Size(100, 200)); EXPECT_FALSE(image.IsThreadSafe()); @@ -375,4 +408,136 @@ TEST(ImageSkiaTest, SourceOnThreadTest) { // Just in case we ever get lumped together with other compilation units. #undef ENABLE_NON_THREAD_SAFE +TEST_F(ImageSkiaTest, Unscaled) { + SkBitmap bitmap; + + // An ImageSkia created with 1x bitmap is unscaled. + ImageSkia image_skia = ImageSkia::CreateFrom1xBitmap(bitmap); + EXPECT_TRUE(image_skia.GetRepresentation(1.0f).unscaled()); + ImageSkiaRep rep_2x(Size(100, 100), 2.0f); + + // When reps for other scales are added, the unscaled image + // becomes scaled. + image_skia.AddRepresentation(rep_2x); + EXPECT_FALSE(image_skia.GetRepresentation(1.0f).unscaled()); + EXPECT_FALSE(image_skia.GetRepresentation(2.0f).unscaled()); +} + +TEST_F(ImageSkiaTest, ArbitraryScaleFactor) { + // Do not test if the ImageSkia doesn't support arbitrary scale factors. + if (!ImageSkia::IsDSFScalingInImageSkiaEnabled()) + return; + + // source is owned by |image| + DynamicSource* source = new DynamicSource(Size(100, 200)); + ImageSkia image(source, gfx::Size(100, 200)); + + image.GetRepresentation(1.5f); + EXPECT_EQ(2.0f, source->GetLastRequestedScaleAndReset()); + std::vector<ImageSkiaRep> image_reps = image.image_reps(); + EXPECT_EQ(2u, image_reps.size()); + + std::vector<float> scale_factors; + for (size_t i = 0; i < image_reps.size(); ++i) { + scale_factors.push_back(image_reps[i].scale()); + } + std::sort(scale_factors.begin(), scale_factors.end()); + EXPECT_EQ(1.5f, scale_factors[0]); + EXPECT_EQ(2.0f, scale_factors[1]); + + // Requesting 1.75 scale factor also falls back to 2.0f and rescale. + // However, the image already has the 2.0f data, so it won't fetch again. + image.GetRepresentation(1.75f); + EXPECT_EQ(0.0f, source->GetLastRequestedScaleAndReset()); + image_reps = image.image_reps(); + EXPECT_EQ(3u, image_reps.size()); + + scale_factors.clear(); + for (size_t i = 0; i < image_reps.size(); ++i) { + scale_factors.push_back(image_reps[i].scale()); + } + std::sort(scale_factors.begin(), scale_factors.end()); + EXPECT_EQ(1.5f, scale_factors[0]); + EXPECT_EQ(1.75f, scale_factors[1]); + EXPECT_EQ(2.0f, scale_factors[2]); + + // 1.25 is falled back to 1.0. + image.GetRepresentation(1.25f); + EXPECT_EQ(1.0f, source->GetLastRequestedScaleAndReset()); + image_reps = image.image_reps(); + EXPECT_EQ(5u, image_reps.size()); + + // Scale factor less than 1.0f will be falled back to 1.0f + image.GetRepresentation(0.75f); + EXPECT_EQ(0.0f, source->GetLastRequestedScaleAndReset()); + image_reps = image.image_reps(); + EXPECT_EQ(6u, image_reps.size()); + + scale_factors.clear(); + for (size_t i = 0; i < image_reps.size(); ++i) { + scale_factors.push_back(image_reps[i].scale()); + } + std::sort(scale_factors.begin(), scale_factors.end()); + EXPECT_EQ(0.75f, scale_factors[0]); + EXPECT_EQ(1.0f, scale_factors[1]); + EXPECT_EQ(1.25f, scale_factors[2]); + EXPECT_EQ(1.5f, scale_factors[3]); + EXPECT_EQ(1.75f, scale_factors[4]); + EXPECT_EQ(2.0f, scale_factors[5]); + + // Scale factor greater than 2.0f is falled back to 2.0f because it's not + // supported. + image.GetRepresentation(3.0f); + EXPECT_EQ(0.0f, source->GetLastRequestedScaleAndReset()); + image_reps = image.image_reps(); + EXPECT_EQ(7u, image_reps.size()); +} + +TEST_F(ImageSkiaTest, ArbitraryScaleFactorWithMissingResource) { + // Do not test if the ImageSkia doesn't support arbitrary scale factors. + if (!ImageSkia::IsDSFScalingInImageSkiaEnabled()) + return; + + ImageSkia image(new FixedSource( + ImageSkiaRep(Size(100, 200), 1.0f)), Size(100, 200)); + + // Requesting 1.5f -- falls back to 2.0f, but couldn't find. It should + // look up 1.0f and then rescale it. + const ImageSkiaRep& rep = image.GetRepresentation(1.5f); + EXPECT_EQ(1.5f, rep.scale()); + EXPECT_EQ(2U, image.image_reps().size()); + EXPECT_EQ(1.0f, image.image_reps()[0].scale()); + EXPECT_EQ(1.5f, image.image_reps()[1].scale()); +} + +TEST_F(ImageSkiaTest, UnscaledImageForArbitraryScaleFactor) { + // Do not test if the ImageSkia doesn't support arbitrary scale factors. + if (!ImageSkia::IsDSFScalingInImageSkiaEnabled()) + return; + + // 0.0f means unscaled. + ImageSkia image(new FixedSource( + ImageSkiaRep(Size(100, 200), 0.0f)), Size(100, 200)); + + // Requesting 2.0f, which should return 1.0f unscaled image. + const ImageSkiaRep& rep = image.GetRepresentation(2.0f); + EXPECT_EQ(1.0f, rep.scale()); + EXPECT_EQ("100x200", rep.pixel_size().ToString()); + EXPECT_TRUE(rep.unscaled()); + EXPECT_EQ(1U, image.image_reps().size()); + + // Same for any other scale factors. + const ImageSkiaRep& rep15 = image.GetRepresentation(1.5f); + EXPECT_EQ(1.0f, rep15.scale()); + EXPECT_EQ("100x200", rep15.pixel_size().ToString()); + EXPECT_TRUE(rep15.unscaled()); + EXPECT_EQ(1U, image.image_reps().size()); + + const ImageSkiaRep& rep12 = image.GetRepresentation(1.2f); + EXPECT_EQ(1.0f, rep12.scale()); + EXPECT_EQ("100x200", rep12.pixel_size().ToString()); + EXPECT_TRUE(rep12.unscaled()); + EXPECT_EQ(1U, image.image_reps().size()); +} + } // namespace gfx diff --git a/chromium/ui/gfx/image/image_skia_util_mac.h b/chromium/ui/gfx/image/image_skia_util_mac.h index 553c3f8741d..fc6a65fb027 100644 --- a/chromium/ui/gfx/image/image_skia_util_mac.h +++ b/chromium/ui/gfx/image/image_skia_util_mac.h @@ -31,10 +31,6 @@ GFX_EXPORT gfx::ImageSkia ImageSkiaFromNSImage(NSImage* image); GFX_EXPORT gfx::ImageSkia ImageSkiaFromResizedNSImage(NSImage* image, NSSize size); -// Resizes |[NSImage imageNamed:@NSApplicationIcon]| to have edge width of -// |size| DIP and returns result as ImageSkia. -GFX_EXPORT gfx::ImageSkia ApplicationIconAtSize(int size); - // Converts to NSImage from ImageSkia. GFX_EXPORT NSImage* NSImageFromImageSkia(const gfx::ImageSkia& image_skia); diff --git a/chromium/ui/gfx/image/image_skia_util_mac.mm b/chromium/ui/gfx/image/image_skia_util_mac.mm index 281badc7423..26c067331a7 100644 --- a/chromium/ui/gfx/image/image_skia_util_mac.mm +++ b/chromium/ui/gfx/image/image_skia_util_mac.mm @@ -78,12 +78,6 @@ gfx::ImageSkia ImageSkiaFromResizedNSImage(NSImage* image, return image_skia; } -gfx::ImageSkia ApplicationIconAtSize(int desired_size) { - NSImage* image = [NSImage imageNamed:@"NSApplicationIcon"]; - return ImageSkiaFromResizedNSImage(image, - NSMakeSize(desired_size, desired_size)); -} - NSImage* NSImageFromImageSkia(const gfx::ImageSkia& image_skia) { if (image_skia.isNull()) return nil; diff --git a/chromium/ui/gfx/image/image_unittest.cc b/chromium/ui/gfx/image/image_unittest.cc index 039cb13f606..ca9efa0003c 100644 --- a/chromium/ui/gfx/image/image_unittest.cc +++ b/chromium/ui/gfx/image/image_unittest.cc @@ -10,10 +10,7 @@ #include "ui/gfx/image/image_skia.h" #include "ui/gfx/image/image_unittest_util.h" -#if defined(TOOLKIT_GTK) -#include <gtk/gtk.h> -#include "ui/gfx/gtk_util.h" -#elif defined(OS_IOS) +#if defined(OS_IOS) #include "base/mac/foundation_util.h" #include "skia/ext/skia_utils_ios.h" #elif defined(OS_MACOSX) @@ -75,7 +72,7 @@ TEST_F(ImageTest, EmptyImage) { // Test constructing a gfx::Image from an empty PlatformImage. TEST_F(ImageTest, EmptyImageFromEmptyPlatformImage) { -#if defined(OS_IOS) || defined(OS_MACOSX) || defined(TOOLKIT_GTK) +#if defined(OS_IOS) || defined(OS_MACOSX) gfx::Image image1(NULL); EXPECT_TRUE(image1.IsEmpty()); EXPECT_EQ(0, image1.Width()); @@ -246,12 +243,24 @@ TEST_F(ImageTest, MultiResolutionPNGToImageSkia) { scales.push_back(1.0f); scales.push_back(2.0f); gfx::ImageSkia image_skia = image.AsImageSkia(); - EXPECT_TRUE(gt::ImageSkiaStructureMatches(image_skia, kSize1x, kSize1x, - scales)); EXPECT_TRUE(gt::IsEqual(bytes1x, image_skia.GetRepresentation(1.0f).sk_bitmap())); EXPECT_TRUE(gt::IsEqual(bytes2x, image_skia.GetRepresentation(2.0f).sk_bitmap())); + EXPECT_TRUE(gt::ImageSkiaStructureMatches(image_skia, kSize1x, kSize1x, + scales)); +#if !defined(OS_IOS) + // IOS does not support arbitrary scale factors. + gfx::ImageSkiaRep rep_1_6x = image_skia.GetRepresentation(1.6f); + ASSERT_FALSE(rep_1_6x.is_null()); + ASSERT_EQ(1.6f, rep_1_6x.scale()); + EXPECT_EQ("40x40", rep_1_6x.pixel_size().ToString()); + + gfx::ImageSkiaRep rep_0_8x = image_skia.GetRepresentation(0.8f); + ASSERT_FALSE(rep_0_8x.is_null()); + ASSERT_EQ(0.8f, rep_0_8x.scale()); + EXPECT_EQ("20x20", rep_0_8x.pixel_size().ToString()); +#endif } TEST_F(ImageTest, MultiResolutionPNGToPlatform) { @@ -436,27 +445,6 @@ TEST_F(ImageTest, PlatformToSkiaToCopy) { delete bitmap; } -#if defined(TOOLKIT_GTK) -TEST_F(ImageTest, SkiaToGdkCopy) { - GdkPixbuf* pixbuf; - - { - gfx::Image image(gt::CreateImageSkia(25, 25)); - pixbuf = image.CopyGdkPixbuf(); - } - - EXPECT_TRUE(pixbuf); - g_object_unref(pixbuf); -} - -TEST_F(ImageTest, SkiaToCairoCreatesGdk) { - gfx::Image image(gt::CreateImageSkia(25, 25)); - EXPECT_FALSE(image.HasRepresentation(gfx::Image::kImageRepGdk)); - EXPECT_TRUE(image.ToCairo()); - EXPECT_TRUE(image.HasRepresentation(gfx::Image::kImageRepGdk)); -} -#endif - #if defined(OS_IOS) TEST_F(ImageTest, SkiaToCocoaTouchCopy) { UIImage* ui_image; @@ -495,9 +483,8 @@ TEST_F(ImageTest, SkBitmapConversionPreservesOrientation) { const int width = 50; const int height = 50; SkBitmap bitmap; - bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height); - bitmap.allocPixels(); - bitmap.eraseRGB(0, 255, 0); + bitmap.allocN32Pixels(width, height); + bitmap.eraseARGB(255, 0, 255, 0); // Paint the upper half of the image in red (lower half is in green). SkCanvas canvas(bitmap); @@ -538,9 +525,7 @@ TEST_F(ImageTest, SkBitmapConversionPreservesTransparency) { const int width = 50; const int height = 50; SkBitmap bitmap; - bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height, 0, - kPremul_SkAlphaType); - bitmap.allocPixels(); + bitmap.allocN32Pixels(width, height); bitmap.eraseARGB(0, 0, 255, 0); // Paint the upper half of the image in red (lower half is transparent). diff --git a/chromium/ui/gfx/image/image_unittest_util.cc b/chromium/ui/gfx/image/image_unittest_util.cc index 4baf30a4cc6..e9a003216d9 100644 --- a/chromium/ui/gfx/image/image_unittest_util.cc +++ b/chromium/ui/gfx/image/image_unittest_util.cc @@ -15,10 +15,7 @@ #include "ui/gfx/codec/png_codec.h" #include "ui/gfx/image/image_skia.h" -#if defined(TOOLKIT_GTK) -#include <gtk/gtk.h> -#include "ui/gfx/gtk_util.h" -#elif defined(OS_IOS) +#if defined(OS_IOS) #include "base/mac/foundation_util.h" #include "base/mac/scoped_cftyperef.h" #include "skia/ext/skia_utils_ios.h" @@ -59,7 +56,7 @@ const SkBitmap CreateBitmap(int width, int height) { SkBitmap bitmap; bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height); bitmap.allocPixels(); - bitmap.eraseRGB(0, 255, 0); + bitmap.eraseARGB(255, 0, 255, 0); return bitmap; } @@ -83,6 +80,8 @@ gfx::Image CreateImage(int width, int height) { } bool IsEqual(const gfx::Image& img1, const gfx::Image& img2) { + img1.AsImageSkia().EnsureRepsForSupportedScales(); + img2.AsImageSkia().EnsureRepsForSupportedScales(); std::vector<gfx::ImageSkiaRep> img1_reps = img1.AsImageSkia().image_reps(); gfx::ImageSkia image_skia2 = img2.AsImageSkia(); if (image_skia2.image_reps().size() != img1_reps.size()) @@ -192,8 +191,6 @@ PlatformImage CreatePlatformImage() { NSImage* image = gfx::SkBitmapToNSImage(bitmap); base::mac::NSObjectRetain(image); return image; -#elif defined(TOOLKIT_GTK) - return gfx::GdkPixbufFromSkBitmap(bitmap); #else return gfx::ImageSkia::CreateFrom1xBitmap(bitmap); #endif @@ -204,8 +201,6 @@ gfx::Image::RepresentationType GetPlatformRepresentationType() { return gfx::Image::kImageRepCocoaTouch; #elif defined(OS_MACOSX) return gfx::Image::kImageRepCocoa; -#elif defined(TOOLKIT_GTK) - return gfx::Image::kImageRepGdk; #else return gfx::Image::kImageRepSkia; #endif @@ -216,8 +211,6 @@ PlatformImage ToPlatformType(const gfx::Image& image) { return image.ToUIImage(); #elif defined(OS_MACOSX) return image.ToNSImage(); -#elif defined(TOOLKIT_GTK) - return image.ToGdkPixbuf(); #else return image.AsImageSkia(); #endif @@ -228,8 +221,6 @@ PlatformImage CopyPlatformType(const gfx::Image& image) { return image.CopyUIImage(); #elif defined(OS_MACOSX) return image.CopyNSImage(); -#elif defined(TOOLKIT_GTK) - return image.CopyGdkPixbuf(); #else return image.AsImageSkia(); #endif @@ -237,16 +228,6 @@ PlatformImage CopyPlatformType(const gfx::Image& image) { #if defined(OS_MACOSX) // Defined in image_unittest_util_mac.mm. -#elif defined(TOOLKIT_GTK) -SkColor GetPlatformImageColor(PlatformImage image, int x, int y) { - int n_channels = gdk_pixbuf_get_n_channels(image); - int rowstride = gdk_pixbuf_get_rowstride(image); - guchar* gdk_pixels = gdk_pixbuf_get_pixels(image); - - guchar* pixel = gdk_pixels + (y * rowstride) + (x * n_channels); - guchar alpha = gdk_pixbuf_get_has_alpha(image) ? pixel[3] : 255; - return SkColorSetARGB(alpha, pixel[0], pixel[1], pixel[2]); -} #else SkColor GetPlatformImageColor(PlatformImage image, int x, int y) { SkBitmap bitmap = *image.bitmap(); @@ -264,7 +245,7 @@ void CheckIsTransparent(SkColor color) { } bool IsPlatformImageValid(PlatformImage image) { -#if defined(OS_MACOSX) || defined(TOOLKIT_GTK) +#if defined(OS_MACOSX) return image != NULL; #else return !image.isNull(); @@ -272,7 +253,7 @@ bool IsPlatformImageValid(PlatformImage image) { } bool PlatformImagesEqual(PlatformImage image1, PlatformImage image2) { -#if defined(OS_MACOSX) || defined(TOOLKIT_GTK) +#if defined(OS_MACOSX) return image1 == image2; #else return image1.BackedBySameObjectAs(image2); diff --git a/chromium/ui/gfx/image/image_unittest_util.h b/chromium/ui/gfx/image/image_unittest_util.h index 4788e4e1009..cac8015eb16 100644 --- a/chromium/ui/gfx/image/image_unittest_util.h +++ b/chromium/ui/gfx/image/image_unittest_util.h @@ -18,8 +18,6 @@ namespace test { typedef UIImage* PlatformImage; #elif defined(OS_MACOSX) typedef NSImage* PlatformImage; -#elif defined(TOOLKIT_GTK) -typedef GdkPixbuf* PlatformImage; #else typedef gfx::ImageSkia PlatformImage; #endif diff --git a/chromium/ui/gfx/image/image_util.cc b/chromium/ui/gfx/image/image_util.cc index 59d631d171d..89a3f8c03b8 100644 --- a/chromium/ui/gfx/image/image_util.cc +++ b/chromium/ui/gfx/image/image_util.cc @@ -12,6 +12,8 @@ namespace gfx { +const uint32_t kMinimumVisibleOpacity = 12; + // The iOS implementations of the JPEG functions are in image_util_ios.mm. #if !defined(OS_IOS) Image ImageFrom1xJPEGEncodedData(const unsigned char* input, @@ -24,7 +26,7 @@ Image ImageFrom1xJPEGEncodedData(const unsigned char* input, } bool JPEG1xEncodedDataFromImage(const Image& image, int quality, - std::vector<unsigned char>* dst) { + std::vector<unsigned char>* dst) { const gfx::ImageSkiaRep& image_skia_rep = image.AsImageSkia().GetRepresentation(1.0f); if (image_skia_rep.scale() != 1.0f) @@ -45,4 +47,57 @@ bool JPEG1xEncodedDataFromImage(const Image& image, int quality, } #endif // !defined(OS_IOS) +bool VisibleMargins(const ImageSkia& image, int* leading, int* trailing) { + *leading = 0; + *trailing = std::max(1, image.width()) - 1; + if (!image.HasRepresentation(1.0)) + return false; + + const ImageSkiaRep& rep = image.GetRepresentation(1.0); + if (rep.is_null()) + return false; + + const SkBitmap& bitmap = rep.sk_bitmap(); + if (bitmap.isNull() || bitmap.width() == 0) + return false; + + if (bitmap.isOpaque()) + return true; + + SkAutoLockPixels l(bitmap); + int inner_min = bitmap.width(); + for (int x = 0; x < bitmap.width(); ++x) { + for (int y = 0; y < bitmap.height(); ++y) { + if (SkColorGetA(bitmap.getColor(x, y)) > kMinimumVisibleOpacity) { + inner_min = x; + break; + } + } + if (inner_min < bitmap.width()) + break; + } + + int inner_max = -1; + for (int x = bitmap.width() - 1; x > inner_min; --x) { + for (int y = 0; y < bitmap.height(); ++y) { + if (SkColorGetA(bitmap.getColor(x, y)) > kMinimumVisibleOpacity) { + inner_max = x; + break; + } + } + if (inner_max > -1) + break; + } + + if (inner_min == bitmap.width()) { + *leading = bitmap.width()/2; + *trailing = bitmap.width()/2 + 1; + return true; + } + + *leading = inner_min; + *trailing = inner_max; + return true; } + +} // namespace gfx diff --git a/chromium/ui/gfx/image/image_util.h b/chromium/ui/gfx/image/image_util.h index f33835ca232..2711850dc96 100644 --- a/chromium/ui/gfx/image/image_util.h +++ b/chromium/ui/gfx/image/image_util.h @@ -12,6 +12,7 @@ namespace gfx { class Image; +class ImageSkia; } namespace gfx { @@ -31,6 +32,22 @@ GFX_EXPORT bool JPEG1xEncodedDataFromImage(const Image& image, int quality, std::vector<unsigned char>* dst); +// Returns the visible (non-transparent) width of the 1x rep of the given +// image. If the image has no transparency, the leading value will be 0 and +// the trailing will be the image width. Return values are in the 1x width +// pixel units. Margins are given in 0-based column format. So if the image +// has only transparent pixels in columns 0, 1, 2, 3, then the leading value +// will be 4. Similarly, if there are all transparent pixels in column +// width-2, width-1, then the trailing margin value will be width-3. +// Returns true if the value is computed from opacity, false if it is a +// default value because of null image, missing Rep, etc. +// This method is only suitable for fairly small images (i.e. 16x16). +// The margins for a completely transparent image will be w/2-1, w/2, but this +// will be an expensive operation: it isn't expected that it will be frequently +// calculated. +GFX_EXPORT bool VisibleMargins(const ImageSkia& image, + int* leading, int* trailing); + } // namespace gfx #endif // UI_GFX_IMAGE_IMAGE_UTIL_H_ diff --git a/chromium/ui/gfx/image/image_util_unittest.cc b/chromium/ui/gfx/image/image_util_unittest.cc index cd9d74898f6..74c39e68fa4 100644 --- a/chromium/ui/gfx/image/image_util_unittest.cc +++ b/chromium/ui/gfx/image/image_util_unittest.cc @@ -9,6 +9,8 @@ #include "base/memory/scoped_ptr.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/skia/include/core/SkBitmap.h" +#include "third_party/skia/include/core/SkRect.h" +#include "ui/gfx/image/image_skia.h" #include "ui/gfx/image/image_unittest_util.h" TEST(ImageUtilTest, JPEGEncodeAndDecode) { @@ -23,3 +25,79 @@ TEST(ImageUtilTest, JPEGEncodeAndDecode) { // JPEG is lossy, so simply check that the image decoded successfully. EXPECT_FALSE(decoded.IsEmpty()); } + +TEST(ImageUtilTest, TestVisibleMargins) { + // Image with non-transparent piece should return margins at those + // columns. + SkBitmap bitmap1; + bitmap1.setConfig(SkBitmap::kARGB_8888_Config, 16, 16); + bitmap1.allocPixels(); + bitmap1.eraseColor(SK_ColorTRANSPARENT); + bitmap1.eraseArea(SkIRect::MakeLTRB(3, 3, 14, 14), SK_ColorYELLOW); + gfx::ImageSkia img = gfx::ImageSkia::CreateFrom1xBitmap(bitmap1); + int x = 0; + int y = 0; + gfx::VisibleMargins(img, &x, &y); + EXPECT_EQ(3, x); + EXPECT_EQ(13, y); + EXPECT_EQ(16, img.width()); + + // Full-width-transparent image should return margins in the center + // of the image. + SkBitmap bitmap2; + bitmap2.setConfig(SkBitmap::kARGB_8888_Config, 16, 16); + bitmap2.allocPixels(); + bitmap2.eraseColor(SK_ColorTRANSPARENT); + gfx::ImageSkia img_transparent = gfx::ImageSkia::CreateFrom1xBitmap(bitmap2); + x = 0; + y = 0; + gfx::VisibleMargins(img_transparent, &x, &y); + EXPECT_EQ(8, x); + EXPECT_EQ(9, y); + EXPECT_EQ(16, img_transparent.width()); + + // Image with non-transparent piece that is skewed to one side should + // return margins at those columns. + SkBitmap bitmap3; + bitmap3.setConfig(SkBitmap::kARGB_8888_Config, 16, 16); + bitmap3.allocPixels(); + bitmap3.eraseColor(SK_ColorTRANSPARENT); + bitmap3.eraseArea(SkIRect::MakeLTRB(3, 3, 5, 5), SK_ColorYELLOW); + gfx::ImageSkia img3 = gfx::ImageSkia::CreateFrom1xBitmap(bitmap3); + x = 0; + y = 0; + gfx::VisibleMargins(img3, &x, &y); + EXPECT_EQ(3, x); + EXPECT_EQ(4, y); + EXPECT_EQ(16, img3.width()); + + // Image with non-transparent piece that is at one edge should + // return margins at those columns. + SkBitmap bitmap4; + bitmap4.setConfig(SkBitmap::kARGB_8888_Config, 16, 16); + bitmap4.allocPixels(); + bitmap4.eraseColor(SK_ColorTRANSPARENT); + bitmap4.eraseArea(SkIRect::MakeLTRB(0, 3, 5, 5), SK_ColorYELLOW); + gfx::ImageSkia img4 = gfx::ImageSkia::CreateFrom1xBitmap(bitmap4); + x = 0; + y = 0; + gfx::VisibleMargins(img4, &x, &y); + EXPECT_EQ(0, x); + EXPECT_EQ(4, y); + EXPECT_EQ(16, img4.width()); + + // Image with non-transparent piece that is at trailing edge should + // return margins at those columns. + SkBitmap bitmap5; + bitmap5.setConfig(SkBitmap::kARGB_8888_Config, 16, 16); + bitmap5.allocPixels(); + bitmap5.eraseColor(SK_ColorTRANSPARENT); + bitmap5.eraseArea(SkIRect::MakeLTRB(4, 3, 16, 16), SK_ColorYELLOW); + gfx::ImageSkia img5 = gfx::ImageSkia::CreateFrom1xBitmap(bitmap5); + x = 0; + y = 0; + gfx::VisibleMargins(img5, &x, &y); + EXPECT_EQ(4, x); + EXPECT_EQ(15, y); + EXPECT_EQ(16, img5.width()); +} diff --git a/chromium/ui/gfx/insets.h b/chromium/ui/gfx/insets.h index a419059a55d..aa4b9eb5270 100644 --- a/chromium/ui/gfx/insets.h +++ b/chromium/ui/gfx/insets.h @@ -1,52 +1,7 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_GFX_INSETS_H_ -#define UI_GFX_INSETS_H_ +// TODO(beng): remove once callsites are patched. +#include "ui/gfx/geometry/insets.h" -#include <string> - -#include "build/build_config.h" -#include "ui/gfx/gfx_export.h" -#include "ui/gfx/insets_base.h" - -#if defined(TOOLKIT_GTK) -typedef struct _GtkBorder GtkBorder; -#endif - -namespace gfx { - -// An integer version of gfx::Insets. -class GFX_EXPORT Insets : public InsetsBase<Insets, int> { - public: - Insets(); - Insets(int top, int left, int bottom, int right); -#if defined(TOOLKIT_GTK) - explicit Insets(const GtkBorder& border); -#endif - - ~Insets(); - - Insets Scale(float scale) const { - return Scale(scale, scale); - } - - Insets Scale(float x_scale, float y_scale) const { - return Insets(static_cast<int>(top() * y_scale), - static_cast<int>(left() * x_scale), - static_cast<int>(bottom() * y_scale), - static_cast<int>(right() * x_scale)); - } - - // Returns a string representation of the insets. - std::string ToString() const; -}; - -#if !defined(COMPILER_MSVC) -extern template class InsetsBase<Insets, int>; -#endif - -} // namespace gfx - -#endif // UI_GFX_INSETS_H_ diff --git a/chromium/ui/gfx/insets_f.h b/chromium/ui/gfx/insets_f.h index 43b801dcb22..d6cfd0392af 100644 --- a/chromium/ui/gfx/insets_f.h +++ b/chromium/ui/gfx/insets_f.h @@ -1,33 +1,7 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_GFX_INSETS_F_H_ -#define UI_GFX_INSETS_F_H_ +// TODO(beng): remove once callsites are patched. +#include "ui/gfx/geometry/insets_f.h" -#include <string> - -#include "build/build_config.h" -#include "ui/gfx/gfx_export.h" -#include "ui/gfx/insets_base.h" - -namespace gfx { - -// A floating versin of gfx::Insets. -class GFX_EXPORT InsetsF : public InsetsBase<InsetsF, float> { - public: - InsetsF(); - InsetsF(float top, float left, float bottom, float right); - ~InsetsF(); - - // Returns a string representation of the insets. - std::string ToString() const; -}; - -#if !defined(COMPILER_MSVC) -extern template class InsetsBase<InsetsF, float>; -#endif - -} // namespace gfx - -#endif // UI_GFX_INSETS_F_H_ diff --git a/chromium/ui/gfx/linux_font_delegate.cc b/chromium/ui/gfx/linux_font_delegate.cc new file mode 100644 index 00000000000..3dfdf942ece --- /dev/null +++ b/chromium/ui/gfx/linux_font_delegate.cc @@ -0,0 +1,23 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/gfx/linux_font_delegate.h" + +namespace { + +gfx::LinuxFontDelegate* g_linux_font_delegate = 0; + +} // namespace + +namespace gfx { + +void LinuxFontDelegate::SetInstance(LinuxFontDelegate* instance) { + g_linux_font_delegate = instance; +} + +const LinuxFontDelegate* LinuxFontDelegate::instance() { + return g_linux_font_delegate; +} + +} // namespace gfx diff --git a/chromium/ui/gfx/linux_font_delegate.h b/chromium/ui/gfx/linux_font_delegate.h new file mode 100644 index 00000000000..8a3070bfd7b --- /dev/null +++ b/chromium/ui/gfx/linux_font_delegate.h @@ -0,0 +1,49 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_GFX_LINUX_FONT_DELEGATE_H_ +#define UI_GFX_LINUX_FONT_DELEGATE_H_ + +#include <string> + +#include "ui/gfx/font_render_params_linux.h" +#include "ui/gfx/gfx_export.h" + +namespace gfx { + +// Allows a Linux platform specific overriding of font preferences. +class GFX_EXPORT LinuxFontDelegate { + public: + virtual ~LinuxFontDelegate() {} + + // Sets the dynamically loaded singleton that provides font preferences. + // This pointer is not owned, and if this method is called a second time, + // the first instance is not deleted. + static void SetInstance(LinuxFontDelegate* instance); + + // Returns a LinuxFontDelegate instance for the toolkit used in + // the user's desktop environment. + // + // Can return NULL, in case no toolkit has been set. (For example, if we're + // running with the "--ash" flag.) + static const LinuxFontDelegate* instance(); + + // Whether we should antialias our text. + virtual bool UseAntialiasing() const = 0; + + // What sort of text hinting should we apply? + virtual FontRenderParams::Hinting GetHintingStyle() const = 0; + + // What sort of subpixel rendering should we perform. + virtual FontRenderParams::SubpixelRendering + GetSubpixelRenderingStyle() const = 0; + + // Returns the default font name for pango style rendering. The format is a + // string of the form "[font name] [font size]". + virtual std::string GetDefaultFontName() const = 0; +}; + +} // namespace gfx + +#endif // UI_GFX_LINUX_FONT_DELEGATE_H_ diff --git a/chromium/ui/gfx/matrix3_f.h b/chromium/ui/gfx/matrix3_f.h index a8ae5b578a8..936c312475b 100644 --- a/chromium/ui/gfx/matrix3_f.h +++ b/chromium/ui/gfx/matrix3_f.h @@ -1,108 +1,7 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_GFX_MATRIX3_F_H_ -#define UI_GFX_MATRIX3_F_H_ +// TODO(beng): remove once callsites are patched. +#include "ui/gfx/geometry/matrix3_f.h" -#include "base/logging.h" -#include "ui/gfx/vector3d_f.h" - -namespace gfx { - -class GFX_EXPORT Matrix3F { - public: - ~Matrix3F(); - - static Matrix3F Zeros(); - static Matrix3F Ones(); - static Matrix3F Identity(); - static Matrix3F FromOuterProduct(const Vector3dF& a, const Vector3dF& bt); - - bool IsEqual(const Matrix3F& rhs) const; - - // Element-wise comparison with given precision. - bool IsNear(const Matrix3F& rhs, float precision) const; - - float get(int i, int j) const { - return data_[MatrixToArrayCoords(i, j)]; - } - - void set(int i, int j, float v) { - data_[MatrixToArrayCoords(i, j)] = v; - } - - void set(float m00, float m01, float m02, - float m10, float m11, float m12, - float m20, float m21, float m22) { - data_[0] = m00; - data_[1] = m01; - data_[2] = m02; - data_[3] = m10; - data_[4] = m11; - data_[5] = m12; - data_[6] = m20; - data_[7] = m21; - data_[8] = m22; - } - - Vector3dF get_column(int i) const { - return Vector3dF( - data_[MatrixToArrayCoords(0, i)], - data_[MatrixToArrayCoords(1, i)], - data_[MatrixToArrayCoords(2, i)]); - } - - void set_column(int i, const Vector3dF& c) { - data_[MatrixToArrayCoords(0, i)] = c.x(); - data_[MatrixToArrayCoords(1, i)] = c.y(); - data_[MatrixToArrayCoords(2, i)] = c.z(); - } - - // Returns an inverse of this if the matrix is non-singular, zero (== Zero()) - // otherwise. - Matrix3F Inverse() const; - - // Value of the determinant of the matrix. - float Determinant() const; - - // Trace (sum of diagonal elements) of the matrix. - float Trace() const { - return data_[MatrixToArrayCoords(0, 0)] + - data_[MatrixToArrayCoords(1, 1)] + - data_[MatrixToArrayCoords(2, 2)]; - } - - // Compute eigenvalues and (optionally) normalized eigenvectors of - // a positive defnite matrix *this. Eigenvectors are computed only if - // non-null |eigenvectors| matrix is passed. If it is NULL, the routine - // will not attempt to compute eigenvectors but will still return eigenvalues - // if they can be computed. - // If eigenvalues cannot be computed (the matrix does not meet constraints) - // the 0-vector is returned. Note that to retrieve eigenvalues, the matrix - // only needs to be symmetric while eigenvectors require it to be - // positive-definite. Passing a non-positive definite matrix will result in - // NaNs in vectors which cannot be computed. - // Eigenvectors are placed as column in |eigenvectors| in order corresponding - // to eigenvalues. - Vector3dF SolveEigenproblem(Matrix3F* eigenvectors) const; - - private: - Matrix3F(); // Uninitialized default. - - static int MatrixToArrayCoords(int i, int j) { - DCHECK(i >= 0 && i < 3); - DCHECK(j >= 0 && j < 3); - return i * 3 + j; - } - - float data_[9]; -}; - -inline bool operator==(const Matrix3F& lhs, const Matrix3F& rhs) { - return lhs.IsEqual(rhs); -} - -} // namespace gfx - -#endif // UI_GFX_MATRIX3_F_H_ diff --git a/chromium/ui/gfx/native_widget_types.h b/chromium/ui/gfx/native_widget_types.h index bd14ed50f77..f7f2808cba9 100644 --- a/chromium/ui/gfx/native_widget_types.h +++ b/chromium/ui/gfx/native_widget_types.h @@ -98,14 +98,7 @@ typedef struct _PangoFontDescription PangoFontDescription; typedef struct _cairo cairo_t; #endif -#if defined(TOOLKIT_GTK) -typedef struct _GdkCursor GdkCursor; -typedef union _GdkEvent GdkEvent; -typedef struct _GdkPixbuf GdkPixbuf; -typedef struct _GdkRegion GdkRegion; -typedef struct _GtkWidget GtkWidget; -typedef struct _GtkWindow GtkWindow; -#elif defined(OS_ANDROID) +#if defined(OS_ANDROID) struct ANativeWindow; namespace ui { class WindowAndroid; @@ -122,12 +115,6 @@ typedef aura::Window* NativeView; typedef aura::Window* NativeWindow; typedef SkRegion* NativeRegion; typedef ui::Event* NativeEvent; -#elif defined(OS_WIN) -typedef HCURSOR NativeCursor; -typedef HWND NativeView; -typedef HWND NativeWindow; -typedef HRGN NativeRegion; -typedef MSG NativeEvent; #elif defined(OS_IOS) typedef void* NativeCursor; typedef UIView* NativeView; @@ -137,13 +124,8 @@ typedef UIEvent* NativeEvent; typedef NSCursor* NativeCursor; typedef NSView* NativeView; typedef NSWindow* NativeWindow; +typedef void* NativeRegion; typedef NSEvent* NativeEvent; -#elif defined(TOOLKIT_GTK) -typedef GdkCursor* NativeCursor; -typedef GtkWidget* NativeView; -typedef GtkWindow* NativeWindow; -typedef GdkRegion* NativeRegion; -typedef GdkEvent* NativeEvent; #elif defined(OS_ANDROID) typedef void* NativeCursor; typedef ui::ViewAndroid* NativeView; @@ -166,11 +148,6 @@ typedef NSFont* NativeFont; typedef NSTextField* NativeEditView; typedef CGContext* NativeDrawingContext; typedef void* NativeViewAccessible; -#elif defined(TOOLKIT_GTK) -typedef PangoFontDescription* NativeFont; -typedef GtkWidget* NativeEditView; -typedef cairo_t* NativeDrawingContext; -typedef void* NativeViewAccessible; #elif defined(USE_CAIRO) typedef PangoFontDescription* NativeFont; typedef void* NativeEditView; @@ -194,8 +171,6 @@ const gfx::NativeCursor kNullCursor = static_cast<gfx::NativeCursor>(NULL); typedef UIImage NativeImageType; #elif defined(OS_MACOSX) typedef NSImage NativeImageType; -#elif defined(TOOLKIT_GTK) -typedef GdkPixbuf NativeImageType; #else typedef SkBitmap NativeImageType; #endif @@ -208,32 +183,8 @@ typedef NativeImageType* NativeImage; // See comment at the top of the file for usage. typedef intptr_t NativeViewId; -#if defined(OS_WIN) && !defined(USE_AURA) -// Convert a NativeViewId to a NativeView. -// -// On Windows, we pass an HWND into the renderer. As stated above, the renderer -// should not be performing operations on the view. -static inline NativeView NativeViewFromId(NativeViewId id) { - return reinterpret_cast<NativeView>(id); -} -#define NativeViewFromIdInBrowser(x) NativeViewFromId(x) -#elif defined(OS_POSIX) || defined(USE_AURA) -// On Mac, Linux and USE_AURA, a NativeView is a pointer to an object, and is -// useless outside the process in which it was created. NativeViewFromId should -// only be used inside the appropriate platform ifdef outside of the browser. -// (NativeViewFromIdInBrowser can be used everywhere in the browser.) If your -// cross-platform design involves a call to NativeViewFromId from outside the -// browser it will never work on Mac or Linux and is fundamentally broken. - -// Please do not call this from outside the browser. It won't work; the name -// should give you a subtle hint. -static inline NativeView NativeViewFromIdInBrowser(NativeViewId id) { - return reinterpret_cast<NativeView>(id); -} -#endif // defined(OS_POSIX) - // PluginWindowHandle is an abstraction wrapping "the types of windows -// used by NPAPI plugins". On Windows it's an HWND, on X it's an X +// used by NPAPI plugins". On Windows it's an HWND, on X it's an X // window id. #if defined(OS_WIN) typedef HWND PluginWindowHandle; @@ -241,11 +192,6 @@ static inline NativeView NativeViewFromIdInBrowser(NativeViewId id) { #elif defined(USE_X11) typedef unsigned long PluginWindowHandle; const PluginWindowHandle kNullPluginWindow = 0; -#elif defined(USE_AURA) && defined(OS_MACOSX) - // Mac-Aura uses NSView-backed GLSurface. Regular Mac does not. - // TODO(dhollowa): Rationalize these two definitions. http://crbug.com/104551. - typedef NSView* PluginWindowHandle; - const PluginWindowHandle kNullPluginWindow = 0; #elif defined(OS_ANDROID) typedef uint64 PluginWindowHandle; const PluginWindowHandle kNullPluginWindow = 0; @@ -253,17 +199,9 @@ static inline NativeView NativeViewFromIdInBrowser(NativeViewId id) { typedef intptr_t PluginWindowHandle; const PluginWindowHandle kNullPluginWindow = 0; #else - // On OS X we don't have windowed plugins. - // We use a NULL/0 PluginWindowHandle in shared code to indicate there - // is no window present, so mirror that behavior here. - // - // The GPU plugin is currently an exception to this rule. As of this - // writing it uses some NPAPI infrastructure, and minimally we need - // to identify the plugin instance via this window handle. When the - // GPU plugin becomes a full-on GPU process, this typedef can be - // returned to a bool. For now we use a type large enough to hold a - // pointer on 64-bit architectures in case we need this capability. - typedef uint64 PluginWindowHandle; + // On Mac we don't have windowed plugins. We use a NULL/0 PluginWindowHandle + // in shared code to indicate there is no window present. + typedef bool PluginWindowHandle; const PluginWindowHandle kNullPluginWindow = 0; #endif @@ -271,20 +209,19 @@ enum SurfaceType { EMPTY, NATIVE_DIRECT, NATIVE_TRANSPORT, - TEXTURE_TRANSPORT + TEXTURE_TRANSPORT, + SURFACE_TYPE_LAST = TEXTURE_TRANSPORT }; struct GLSurfaceHandle { GLSurfaceHandle() : handle(kNullPluginWindow), transport_type(EMPTY), - parent_gpu_process_id(0), parent_client_id(0) { } GLSurfaceHandle(PluginWindowHandle handle_, SurfaceType transport_) : handle(handle_), transport_type(transport_), - parent_gpu_process_id(0), parent_client_id(0) { DCHECK(!is_null() || handle == kNullPluginWindow); DCHECK(transport_type != TEXTURE_TRANSPORT || @@ -297,7 +234,6 @@ struct GLSurfaceHandle { } PluginWindowHandle handle; SurfaceType transport_type; - int parent_gpu_process_id; uint32 parent_client_id; }; diff --git a/chromium/ui/gfx/nine_image_painter.cc b/chromium/ui/gfx/nine_image_painter.cc new file mode 100644 index 00000000000..24a3e3b3627 --- /dev/null +++ b/chromium/ui/gfx/nine_image_painter.cc @@ -0,0 +1,157 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/gfx/nine_image_painter.h" + +#include <limits> + +#include "third_party/skia/include/core/SkPaint.h" +#include "third_party/skia/include/core/SkRect.h" +#include "third_party/skia/include/core/SkScalar.h" +#include "ui/gfx/canvas.h" +#include "ui/gfx/geometry/rect_conversions.h" +#include "ui/gfx/geometry/safe_integer_conversions.h" +#include "ui/gfx/image/image_skia_operations.h" +#include "ui/gfx/insets.h" +#include "ui/gfx/rect.h" +#include "ui/gfx/scoped_canvas.h" +#include "ui/gfx/skia_util.h" + +namespace gfx { + +namespace { + +// The following functions width and height of the image in pixels for the +// scale factor in the Canvas. +int ImageWidthInPixels(const ImageSkia& i, Canvas* c) { + return i.GetRepresentation(c->image_scale()).pixel_width(); +} + +int ImageHeightInPixels(const ImageSkia& i, Canvas* c) { + return i.GetRepresentation(c->image_scale()).pixel_height(); +} + +// Stretches the given image over the specified canvas area. +void Fill(Canvas* c, + const ImageSkia& i, + int x, + int y, + int w, + int h, + const SkPaint& paint) { + c->DrawImageIntInPixel(i, 0, 0, ImageWidthInPixels(i, c), + ImageHeightInPixels(i, c), x, y, w, h, false, paint); +} + +} // namespace + +NineImagePainter::NineImagePainter(const std::vector<ImageSkia>& images) { + DCHECK_EQ(arraysize(images_), images.size()); + for (size_t i = 0; i < arraysize(images_); ++i) + images_[i] = images[i]; +} + +NineImagePainter::NineImagePainter(const ImageSkia& image, + const Insets& insets) { + DCHECK_GE(image.width(), insets.width()); + DCHECK_GE(image.height(), insets.height()); + + // Extract subsets of the original image to match the |images_| format. + const int x[] = + { 0, insets.left(), image.width() - insets.right(), image.width() }; + const int y[] = + { 0, insets.top(), image.height() - insets.bottom(), image.height() }; + + for (size_t j = 0; j < 3; ++j) { + for (size_t i = 0; i < 3; ++i) { + images_[i + j * 3] = ImageSkiaOperations::ExtractSubset(image, + Rect(x[i], y[j], x[i + 1] - x[i], y[j + 1] - y[j])); + } + } +} + +NineImagePainter::~NineImagePainter() { +} + +bool NineImagePainter::IsEmpty() const { + return images_[0].isNull(); +} + +Size NineImagePainter::GetMinimumSize() const { + return IsEmpty() ? Size() : Size( + images_[0].width() + images_[1].width() + images_[2].width(), + images_[0].height() + images_[3].height() + images_[6].height()); +} + +void NineImagePainter::Paint(Canvas* canvas, const Rect& bounds) { + // When no alpha value is specified, use default value of 100% opacity. + Paint(canvas, bounds, std::numeric_limits<uint8>::max()); +} + +void NineImagePainter::Paint(Canvas* canvas, + const Rect& bounds, + const uint8 alpha) { + if (IsEmpty()) + return; + + ScopedCanvas scoped_canvas(canvas); + canvas->Translate(bounds.OffsetFromOrigin()); + + SkPaint paint; + paint.setAlpha(alpha); + + // Get the current transform from the canvas and apply it to the logical + // bounds passed in. This will give us the pixel bounds which can be used + // to draw the images at the correct locations. + // We should not scale the bounds by the canvas->image_scale() as that can be + // different from the real scale in the canvas transform. + SkMatrix matrix = canvas->sk_canvas()->getTotalMatrix(); + SkRect scaled_rect; + matrix.mapRect(&scaled_rect, RectToSkRect(bounds)); + + int scaled_width = gfx::ToCeiledInt(SkScalarToFloat(scaled_rect.width())); + int scaled_height = gfx::ToCeiledInt(SkScalarToFloat(scaled_rect.height())); + + // In case the corners and edges don't all have the same width/height, we draw + // the center first, and extend it out in all directions to the edges of the + // images with the smallest widths/heights. This way there will be no + // unpainted areas, though some corners or edges might overlap the center. + int i0w = ImageWidthInPixels(images_[0], canvas); + int i2w = ImageWidthInPixels(images_[2], canvas); + int i3w = ImageWidthInPixels(images_[3], canvas); + int i5w = ImageWidthInPixels(images_[5], canvas); + int i6w = ImageWidthInPixels(images_[6], canvas); + int i8w = ImageWidthInPixels(images_[8], canvas); + + int i4x = std::min(std::min(i0w, i3w), i6w); + int i4w = scaled_width - i4x - std::min(std::min(i2w, i5w), i8w); + + int i0h = ImageHeightInPixels(images_[0], canvas); + int i1h = ImageHeightInPixels(images_[1], canvas); + int i2h = ImageHeightInPixels(images_[2], canvas); + int i6h = ImageHeightInPixels(images_[6], canvas); + int i7h = ImageHeightInPixels(images_[7], canvas); + int i8h = ImageHeightInPixels(images_[8], canvas); + + int i4y = std::min(std::min(i0h, i1h), i2h); + int i4h = scaled_height - i4y - std::min(std::min(i6h, i7h), i8h); + if (!images_[4].isNull()) + Fill(canvas, images_[4], i4x, i4y, i4w, i4h, paint); + canvas->DrawImageIntInPixel(images_[0], 0, 0, i0w, i0h, + 0, 0, i0w, i0h, false, paint); + Fill(canvas, images_[1], i0w, 0, scaled_width - i0w - i2w, i1h, paint); + canvas->DrawImageIntInPixel(images_[2], 0, 0, i2w, i2h, scaled_width - i2w, + 0, i2w, i2h, false, paint); + Fill(canvas, images_[3], 0, i0h, i3w, scaled_height - i0h - i6h, paint); + Fill(canvas, images_[5], scaled_width - i5w, i2h, i5w, + scaled_height - i2h - i8h, paint); + canvas->DrawImageIntInPixel(images_[6], 0, 0, i6w, i6h, 0, + scaled_height - i6h, i6w, i6h, false, paint); + Fill(canvas, images_[7], i6w, scaled_height - i7h, scaled_width - i6w - i8w, + i7h, paint); + canvas->DrawImageIntInPixel(images_[8], 0, 0, i8w, i8h, scaled_width - i8w, + scaled_height - i8h, i8w, i8h, false, paint); +} + +} // namespace gfx diff --git a/chromium/ui/gfx/nine_image_painter.h b/chromium/ui/gfx/nine_image_painter.h new file mode 100644 index 00000000000..46458d5cb28 --- /dev/null +++ b/chromium/ui/gfx/nine_image_painter.h @@ -0,0 +1,42 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_GFX_NINE_IMAGE_PAINTER_H_ +#define UI_GFX_NINE_IMAGE_PAINTER_H_ + +#include "base/logging.h" +#include "ui/gfx/gfx_export.h" +#include "ui/gfx/image/image_skia.h" + +namespace gfx { + +class Canvas; +class Insets; +class Rect; + +class GFX_EXPORT NineImagePainter { + public: + explicit NineImagePainter(const std::vector<ImageSkia>& images); + NineImagePainter(const ImageSkia& image, const Insets& insets); + ~NineImagePainter(); + + bool IsEmpty() const; + Size GetMinimumSize() const; + void Paint(Canvas* canvas, const Rect& bounds); + void Paint(Canvas* canvas, const Rect& bounds, uint8 alpha); + + private: + // Images are numbered as depicted below. + // ____________________ + // |__i0__|__i1__|__i2__| + // |__i3__|__i4__|__i5__| + // |__i6__|__i7__|__i8__| + ImageSkia images_[9]; + + DISALLOW_COPY_AND_ASSIGN(NineImagePainter); +}; + +} // namespace gfx + +#endif // UI_GFX_NINE_IMAGE_PAINTER_H_ diff --git a/chromium/ui/gfx/overlay_transform.h b/chromium/ui/gfx/overlay_transform.h new file mode 100644 index 00000000000..1e27010933e --- /dev/null +++ b/chromium/ui/gfx/overlay_transform.h @@ -0,0 +1,24 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_GFX_OVERLAY_TRANSFORM_H_ +#define UI_GFX_OVERLAY_TRANSFORM_H_ + +namespace gfx { + +// Describes transformation to be applied to the buffer before presenting +// to screen. +enum OverlayTransform { + OVERLAY_TRANSFORM_INVALID, + OVERLAY_TRANSFORM_NONE, + OVERLAY_TRANSFORM_FLIP_HORIZONTAL, + OVERLAY_TRANSFORM_FLIP_VERTICAL, + OVERLAY_TRANSFORM_ROTATE_90, + OVERLAY_TRANSFORM_ROTATE_180, + OVERLAY_TRANSFORM_ROTATE_270, +}; + +} // namespace gfx + +#endif // UI_GFX_OVERLAY_TRANSFORM_H_ diff --git a/chromium/ui/gfx/ozone/OWNERS b/chromium/ui/gfx/ozone/OWNERS deleted file mode 100644 index 77f21b5cae7..00000000000 --- a/chromium/ui/gfx/ozone/OWNERS +++ /dev/null @@ -1 +0,0 @@ -rjkroege@chromium.org diff --git a/chromium/ui/gfx/ozone/dri/dri_skbitmap.cc b/chromium/ui/gfx/ozone/dri/dri_skbitmap.cc deleted file mode 100644 index 4fbe0f54d89..00000000000 --- a/chromium/ui/gfx/ozone/dri/dri_skbitmap.cc +++ /dev/null @@ -1,208 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/gfx/ozone/dri/dri_skbitmap.h" - -#include <errno.h> -#include <sys/mman.h> -#include <sys/types.h> -#include <xf86drm.h> - -#include "base/compiler_specific.h" -#include "base/logging.h" -#include "third_party/skia/include/core/SkPixelRef.h" - -namespace gfx { - -namespace { - -void DestroyDumbBuffer(int fd, uint32_t handle) { - struct drm_mode_destroy_dumb destroy_request; - destroy_request.handle = handle; - drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_request); -} - -// Special DRM implementation of a SkPixelRef. The DRM allocator will create a -// SkPixelRef for the backing pixels. It will then associate the SkPixelRef with -// the SkBitmap. SkBitmap will access the allocated memory by locking the pixels -// in the SkPixelRef. -// At the end of its life the SkPixelRef is responsible for deallocating the -// pixel memory. -class DriSkPixelRef : public SkPixelRef { - public: - DriSkPixelRef(void* pixels, - SkColorTable* color_table_, - size_t size, - int fd, - uint32_t handle); - virtual ~DriSkPixelRef(); - - virtual void* onLockPixels(SkColorTable** ct) OVERRIDE; - virtual void onUnlockPixels() OVERRIDE; - - SK_DECLARE_UNFLATTENABLE_OBJECT() - private: - // Raw pointer to the pixel memory. - void* pixels_; - - // Optional color table associated with the pixel memory. - SkColorTable* color_table_; - - // Size of the allocated memory. - size_t size_; - - // File descriptor to the graphics card used to allocate/deallocate the - // memory. - int fd_; - - // Handle for the allocated memory. - uint32_t handle_; - - DISALLOW_COPY_AND_ASSIGN(DriSkPixelRef); -}; - -//////////////////////////////////////////////////////////////////////////////// -// DriSkPixelRef implementation - -DriSkPixelRef::DriSkPixelRef( - void* pixels, - SkColorTable* color_table, - size_t size, - int fd, - uint32_t handle) - : pixels_(pixels), - color_table_(color_table), - size_(size), - fd_(fd), - handle_(handle) { -} - -DriSkPixelRef::~DriSkPixelRef() { - munmap(pixels_, size_); - DestroyDumbBuffer(fd_, handle_); -} - -void* DriSkPixelRef::onLockPixels(SkColorTable** ct) { - *ct = color_table_; - return pixels_; -} - -void DriSkPixelRef::onUnlockPixels() { -} - -} // namespace - -// Allocates pixel memory for a SkBitmap using DRM dumb buffers. -class DriAllocator : public SkBitmap::Allocator { - public: - DriAllocator(); - - virtual bool allocPixelRef(SkBitmap* bitmap, - SkColorTable* color_table) OVERRIDE; - - private: - bool AllocatePixels(DriSkBitmap* bitmap, SkColorTable* color_table); - - DISALLOW_COPY_AND_ASSIGN(DriAllocator); -}; - -//////////////////////////////////////////////////////////////////////////////// -// DriAllocator implementation - -DriAllocator::DriAllocator() { -} - -bool DriAllocator::allocPixelRef(SkBitmap* bitmap, - SkColorTable* color_table) { - return AllocatePixels(static_cast<DriSkBitmap*>(bitmap), color_table); -} - -bool DriAllocator::AllocatePixels(DriSkBitmap* bitmap, - SkColorTable* color_table) { - struct drm_mode_create_dumb request; - request.width = bitmap->width(); - request.height = bitmap->height(); - request.bpp = bitmap->bytesPerPixel() << 3; - request.flags = 0; - - if (drmIoctl(bitmap->get_fd(), DRM_IOCTL_MODE_CREATE_DUMB, &request) < 0) { - DLOG(ERROR) << "Cannot create dumb buffer (" << errno << ") " - << strerror(errno); - return false; - } - - CHECK(request.size == bitmap->getSize()); - - bitmap->set_handle(request.handle); - - struct drm_mode_map_dumb map_request; - map_request.handle = bitmap->get_handle(); - if (drmIoctl(bitmap->get_fd(), DRM_IOCTL_MODE_MAP_DUMB, &map_request)) { - DLOG(ERROR) << "Cannot prepare dumb buffer for mapping (" << errno << ") " - << strerror(errno); - DestroyDumbBuffer(bitmap->get_fd(), bitmap->get_handle()); - return false; - } - - void* pixels = mmap(0, - bitmap->getSize(), - PROT_READ | PROT_WRITE, - MAP_SHARED, - bitmap->get_fd(), - map_request.offset); - if (pixels == MAP_FAILED) { - DLOG(ERROR) << "Cannot mmap dumb buffer (" << errno << ") " - << strerror(errno); - DestroyDumbBuffer(bitmap->get_fd(), bitmap->get_handle()); - return false; - } - - bitmap->setPixelRef(new DriSkPixelRef( - pixels, - color_table, - bitmap->getSize(), - bitmap->get_fd(), - bitmap->get_handle()))->unref(); - bitmap->lockPixels(); - - return true; -} - -//////////////////////////////////////////////////////////////////////////////// -// DriSkBitmap implementation - -DriSkBitmap::DriSkBitmap(int fd) - : fd_(fd), - handle_(0), - framebuffer_(0) { -} - -DriSkBitmap::~DriSkBitmap() { -} - -bool DriSkBitmap::Initialize() { - DriAllocator drm_allocator; - return allocPixels(&drm_allocator, NULL); -} - -uint8_t DriSkBitmap::GetColorDepth() const { - switch (config()) { - case SkBitmap::kNo_Config: - case SkBitmap::kA8_Config: - return 0; - case SkBitmap::kIndex8_Config: - return 8; - case SkBitmap::kRGB_565_Config: - return 16; - case SkBitmap::kARGB_4444_Config: - return 12; - case SkBitmap::kARGB_8888_Config: - return 24; - default: - NOTREACHED(); - return 0; - } -} - -} // namespace gfx diff --git a/chromium/ui/gfx/ozone/dri/dri_skbitmap.h b/chromium/ui/gfx/ozone/dri/dri_skbitmap.h deleted file mode 100644 index f5b8f9455fa..00000000000 --- a/chromium/ui/gfx/ozone/dri/dri_skbitmap.h +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_GFX_OZONE_DRI_DRI_SKBITMAP_H_ -#define UI_GFX_OZONE_DRI_DRI_SKBITMAP_H_ - -#include "base/basictypes.h" -#include "third_party/skia/include/core/SkBitmap.h" -#include "ui/gfx/gfx_export.h" - -namespace gfx { - -// Extend the SkBitmap interface to keep track of additional parameters used by -// the DRM stack when allocating buffers. -class GFX_EXPORT DriSkBitmap : public SkBitmap { - public: - DriSkBitmap(int fd); - virtual ~DriSkBitmap(); - - // Allocates the backing pixels using DRI. - // Return true on success, false otherwise. - virtual bool Initialize(); - - uint32_t get_handle() const { return handle_; }; - - uint32_t get_framebuffer() const { return framebuffer_; }; - - int get_fd() const { return fd_; }; - - // Return the color depth of a pixel in this buffer. - uint8_t GetColorDepth() const; - - private: - friend class DriAllocator; - friend class HardwareDisplayController; - - void set_handle(uint32_t handle) { handle_ = handle; }; - void set_framebuffer(uint32_t framebuffer) { framebuffer_ = framebuffer; }; - - // File descriptor used by the DRI allocator to request buffers from the DRI - // stack. - int fd_; - - // Buffer handle used by the DRI allocator. - uint32_t handle_; - - // Buffer ID used by the DRI modesettings API. This is set when the buffer is - // registered with the CRTC. - uint32_t framebuffer_; - - DISALLOW_COPY_AND_ASSIGN(DriSkBitmap); -}; - -} // namespace gfx - -#endif // UI_GFX_OZONE_DRI_DRI_SKBITMAP_H_ diff --git a/chromium/ui/gfx/ozone/dri/dri_surface.cc b/chromium/ui/gfx/ozone/dri/dri_surface.cc deleted file mode 100644 index c9b6af4b562..00000000000 --- a/chromium/ui/gfx/ozone/dri/dri_surface.cc +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/gfx/ozone/dri/dri_surface.h" - -#include <errno.h> -#include <sys/mman.h> -#include <sys/types.h> -#include <xf86drm.h> - -#include "base/logging.h" -#include "third_party/skia/include/core/SkBitmap.h" -#include "third_party/skia/include/core/SkBitmapDevice.h" -#include "third_party/skia/include/core/SkCanvas.h" -#include "ui/gfx/ozone/dri/dri_skbitmap.h" -#include "ui/gfx/ozone/dri/hardware_display_controller.h" -#include "ui/gfx/skia_util.h" - -namespace gfx { - -namespace { - -// Extends the SkBitmapDevice to allow setting the SkPixelRef. We use the setter -// to change the SkPixelRef such that the device always points to the -// backbuffer. -class CustomSkBitmapDevice : public SkBitmapDevice { - public: - CustomSkBitmapDevice(const SkBitmap& bitmap) : SkBitmapDevice(bitmap) {} - virtual ~CustomSkBitmapDevice() {} - - void SetPixelRef(SkPixelRef* pixel_ref) { setPixelRef(pixel_ref, 0); } - - private: - DISALLOW_COPY_AND_ASSIGN(CustomSkBitmapDevice); -}; - -} // namespace - -//////////////////////////////////////////////////////////////////////////////// -// DriSurface implementation - -DriSurface::DriSurface( - HardwareDisplayController* controller) - : controller_(controller), - bitmaps_(), - front_buffer_(0) { -} - -DriSurface::~DriSurface() { -} - -bool DriSurface::Initialize() { - for (int i = 0; i < 2; ++i) { - bitmaps_[i].reset(CreateBuffer()); - // TODO(dnicoara) Should select the configuration based on what the - // underlying system supports. - bitmaps_[i]->setConfig(SkBitmap::kARGB_8888_Config, - controller_->get_mode().hdisplay, - controller_->get_mode().vdisplay); - - if (!bitmaps_[i]->Initialize()) { - return false; - } - } - - skia_device_ = skia::AdoptRef( - new CustomSkBitmapDevice(*bitmaps_[front_buffer_ ^ 1].get())); - skia_canvas_ = skia::AdoptRef(new SkCanvas(skia_device_.get())); - - return true; -} - -uint32_t DriSurface::GetFramebufferId() const { - CHECK(bitmaps_[0].get() && bitmaps_[1].get()); - return bitmaps_[front_buffer_ ^ 1]->get_framebuffer(); -} - -// This call is made after the hardware just started displaying our back buffer. -// We need to update our pointer reference and synchronize the two buffers. -void DriSurface::SwapBuffers() { - CHECK(bitmaps_[0].get() && bitmaps_[1].get()); - - // Update our front buffer pointer. - front_buffer_ ^= 1; - - // Unlocking will unset the pixel pointer, so it won't be pointing to the old - // PixelRef. - skia_device_->accessBitmap(false).unlockPixels(); - // Update the backing pixels for the bitmap device. - static_cast<CustomSkBitmapDevice*>(skia_device_.get())->SetPixelRef( - bitmaps_[front_buffer_ ^ 1]->pixelRef()); - // Locking the pixels will set the pixel pointer based on the PixelRef value. - skia_device_->accessBitmap(false).lockPixels(); - - SkIRect device_damage; - skia_canvas_->getClipDeviceBounds(&device_damage); - SkRect damage = SkRect::Make(device_damage); - - skia_canvas_->drawBitmapRectToRect(*bitmaps_[front_buffer_].get(), - &damage, - damage); -} - -SkCanvas* DriSurface::GetDrawableForWidget() { - return skia_canvas_.get(); -} - -DriSkBitmap* DriSurface::CreateBuffer() { - return new DriSkBitmap(controller_->get_fd()); -} - -} // namespace gfx diff --git a/chromium/ui/gfx/ozone/dri/dri_surface.h b/chromium/ui/gfx/ozone/dri/dri_surface.h deleted file mode 100644 index 53258cf8086..00000000000 --- a/chromium/ui/gfx/ozone/dri/dri_surface.h +++ /dev/null @@ -1,168 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_GFX_OZONE_DRI_DRI_SURFACE_H_ -#define UI_GFX_OZONE_DRI_DRI_SURFACE_H_ - -#include "base/compiler_specific.h" -#include "base/memory/scoped_ptr.h" -#include "ui/gfx/gfx_export.h" -#include "ui/gfx/skia_util.h" - -class SkBitmapDevice; -class SkCanvas; - -namespace gfx { - -class DriSkBitmap; -class HardwareDisplayController; - -// DriSurface is used to represent a surface that can be scanned out -// to a monitor. It will store the internal state associated with the drawing -// surface associated with it. DriSurface also performs all the needed -// operations to initialize and update the drawing surface. -// -// The implementation uses dumb buffers, which is used for software rendering. -// The intent is to have one DriSurface implementation for a -// HardwareDisplayController. -// -// DoubleBufferedSurface is intended to be the software analog to -// EGLNativeSurface while DriSurface is intended to provide the glue -// necessary to initialize and display the surface to the screen. -// -// The typical usage pattern is: -// ----------------------------------------------------------------------------- -// HardwareDisplayController controller; -// // Initialize controller -// -// DriSurface* surface = new DriSurface(controller); -// surface.Initialize(); -// controller.BindSurfaceToController(surface); -// -// while (true) { -// SkCanvas* canvas = surface->GetDrawableForWidget(); -// DrawStuff(canvas); -// controller.SchedulePageFlip(); -// -// Wait for page flip event. The DRM page flip handler will call -// surface.SwapBuffers(); -// } -// -// delete surface; -// ----------------------------------------------------------------------------- -// In the above example the wait consists of reading a DRM pageflip event from -// the graphics card file descriptor. This is done by calling |drmHandleEvent|, -// which will read and process the event. |drmHandleEvent| will call a callback -// registered by |SchedulePageFlip| which will update the internal state. -// -// |SchedulePageFlip| can also be used to limit drawing to the screen's vsync -// since page flips only happen on vsync. In a threaded environment a message -// loop would listen on the graphics card file descriptor for an event and -// |drmHandleEvent| would be called from the message loop. The event handler -// would also be responsible for updating the renderer's state and signal that -// it is OK to start drawing the next frame. -// -// The following example will illustrate the system state transitions in one -// iteration of the above loop. -// -// 1. Both buffers contain the same image with b[0] being the front buffer -// (star will represent the frontbuffer). -// ------- ------- -// | | | | -// | | | | -// | | | | -// | | | | -// ------- ------- -// b[0]* b[1] -// -// 2. Call |GetBackbuffer| to get a SkCanvas wrapper for the backbuffer and draw -// to it. -// ------- ------- -// | | | | -// | | | d | -// | | | | -// | | | | -// ------- ------- -// b[0]* b[1] -// -// 3. Call |SchedulePageFlip| to display the backbuffer. At this point we can't -// modify b[0] because it is the frontbuffer and we can't modify b[1] since it -// has been scheduled for pageflip. If we do draw in b[1] it is possible that -// the pageflip and draw happen at the same time and we could get tearing. -// -// 4. The pageflip callback is called which will call |SwapSurfaces|. Before -// |SwapSurfaces| is called the state is as following from the hardware's -// perspective: -// ------- ------- -// | | | | -// | | | d | -// | | | | -// | | | | -// ------- ------- -// b[0] b[1]* -// -// 5. |SwapSurfaces| will update out internal reference to the front buffer and -// synchronize the damaged area such that both buffers are identical. The -// damaged area is used from the SkCanvas clip. -// ------- ------- -// | | | | -// | d | | d | -// | | | | -// | | | | -// ------- ------- -// b[0] b[1]* -// -// The synchronization consists of copying the damaged area from the frontbuffer -// to the backbuffer. -// -// At this point we're back to step 1 and can start a new draw iteration. -class GFX_EXPORT DriSurface { - public: - DriSurface(HardwareDisplayController* controller); - - virtual ~DriSurface(); - - // Used to allocate all necessary buffers for this surface. If the - // initialization succeeds, the device is ready to be used for drawing - // operations. - // Returns true if the initialization is successful, false otherwise. - bool Initialize(); - - // Returns the ID of the current backbuffer. - uint32_t GetFramebufferId() const; - - // Synchronizes and swaps the back buffer with the front buffer. - void SwapBuffers(); - - // Get a Skia canvas for a backbuffer. - SkCanvas* GetDrawableForWidget(); - - private: - friend class HardwareDisplayController; - - // Used to create the backing buffers. - virtual DriSkBitmap* CreateBuffer(); - - // Stores DRM information for this output device (connector, encoder, last - // CRTC state). - HardwareDisplayController* controller_; - - // The actual buffers used for painting. - scoped_ptr<DriSkBitmap> bitmaps_[2]; - - // BitmapDevice for the current backbuffer. - skia::RefPtr<SkBitmapDevice> skia_device_; - - // Canvas for the current backbuffer. - skia::RefPtr<SkCanvas> skia_canvas_; - - // Keeps track of which bitmap is |buffers_| is the frontbuffer. - int front_buffer_; - - DISALLOW_COPY_AND_ASSIGN(DriSurface); -}; - -} // namespace gfx - -#endif // UI_GFX_OZONE_DRI_DRI_SURFACE_H_ diff --git a/chromium/ui/gfx/ozone/dri/dri_surface_factory.cc b/chromium/ui/gfx/ozone/dri/dri_surface_factory.cc deleted file mode 100644 index 4c201373942..00000000000 --- a/chromium/ui/gfx/ozone/dri/dri_surface_factory.cc +++ /dev/null @@ -1,317 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/gfx/ozone/dri/dri_surface_factory.h" - -#include <drm.h> -#include <errno.h> -#include <xf86drm.h> - -#include "base/message_loop/message_loop.h" -#include "third_party/skia/include/core/SkBitmap.h" -#include "third_party/skia/include/core/SkDevice.h" -#include "ui/gfx/native_widget_types.h" -#include "ui/gfx/ozone/dri/dri_skbitmap.h" -#include "ui/gfx/ozone/dri/dri_surface.h" -#include "ui/gfx/ozone/dri/dri_vsync_provider.h" -#include "ui/gfx/ozone/dri/dri_wrapper.h" -#include "ui/gfx/ozone/dri/hardware_display_controller.h" - -namespace gfx { - -namespace { - -const char kDefaultGraphicsCardPath[] = "/dev/dri/card0"; -const char kDPMSProperty[] = "DPMS"; - -const gfx::AcceleratedWidget kDefaultWidgetHandle = 1; - -// DRM callback on page flip events. This callback is triggered after the -// page flip has happened and the backbuffer is now the new frontbuffer -// The old frontbuffer is no longer used by the hardware and can be used for -// future draw operations. -// -// |device| will contain a reference to the |DriSurface| object which -// the event belongs to. -// -// TODO(dnicoara) When we have a FD handler for the DRM calls in the message -// loop, we can move this function in the handler. -void HandlePageFlipEvent(int fd, - unsigned int frame, - unsigned int seconds, - unsigned int useconds, - void* controller) { - static_cast<HardwareDisplayController*>(controller) - ->OnPageFlipEvent(frame, seconds, useconds); -} - -uint32_t GetDriProperty(int fd, drmModeConnector* connector, const char* name) { - for (int i = 0; i < connector->count_props; ++i) { - drmModePropertyPtr property = drmModeGetProperty(fd, connector->props[i]); - if (!property) - continue; - - if (strcmp(property->name, name) == 0) { - uint32_t id = property->prop_id; - drmModeFreeProperty(property); - return id; - } - - drmModeFreeProperty(property); - } - return 0; -} - -uint32_t GetCrtc(int fd, drmModeRes* resources, drmModeConnector* connector) { - // If the connector already has an encoder try to re-use. - if (connector->encoder_id) { - drmModeEncoder* encoder = drmModeGetEncoder(fd, connector->encoder_id); - if (encoder) { - if (encoder->crtc_id) { - uint32_t crtc = encoder->crtc_id; - drmModeFreeEncoder(encoder); - return crtc; - } - drmModeFreeEncoder(encoder); - } - } - - // Try to find an encoder for the connector. - for (int i = 0; i < connector->count_encoders; ++i) { - drmModeEncoder* encoder = drmModeGetEncoder(fd, connector->encoders[i]); - if (!encoder) - continue; - - for (int j = 0; j < resources->count_crtcs; ++j) { - // Check if the encoder is compatible with this CRTC - if (!(encoder->possible_crtcs & (1 << j))) - continue; - - drmModeFreeEncoder(encoder); - return resources->crtcs[j]; - } - } - - return 0; -} - -} // namespace - -DriSurfaceFactory::DriSurfaceFactory() - : drm_(), - state_(UNINITIALIZED), - controller_() { -} - -DriSurfaceFactory::~DriSurfaceFactory() { - if (state_ == INITIALIZED) - ShutdownHardware(); -} - -SurfaceFactoryOzone::HardwareState -DriSurfaceFactory::InitializeHardware() { - CHECK(state_ == UNINITIALIZED); - - // TODO(dnicoara): Short-cut right now. What we want is to look at all the - // graphics devices available and select the primary one. - drm_.reset(CreateWrapper()); - if (drm_->get_fd() < 0) { - LOG(ERROR) << "Cannot open graphics card '" - << kDefaultGraphicsCardPath << "': " << strerror(errno); - state_ = FAILED; - return state_; - } - - state_ = INITIALIZED; - return state_; -} - -void DriSurfaceFactory::ShutdownHardware() { - CHECK(state_ == INITIALIZED); - - controller_.reset(); - drm_.reset(); - - state_ = UNINITIALIZED; -} - -gfx::AcceleratedWidget DriSurfaceFactory::GetAcceleratedWidget() { - CHECK(state_ != FAILED); - - // TODO(dnicoara) When there's more information on which display we want, - // then we can return the widget associated with the display. - // For now just assume we have 1 display device and return it. - if (!controller_.get()) - controller_.reset(new HardwareDisplayController()); - - // TODO(dnicoara) We only have 1 display for now, so only 1 AcceleratedWidget. - // When we'll support multiple displays this needs to be changed to return a - // different handle for every display. - return kDefaultWidgetHandle; -} - -gfx::AcceleratedWidget DriSurfaceFactory::RealizeAcceleratedWidget( - gfx::AcceleratedWidget w) { - CHECK(state_ == INITIALIZED); - // TODO(dnicoara) Once we can handle multiple displays this needs to be - // changed. - CHECK(w == kDefaultWidgetHandle); - - CHECK(controller_->get_state() == - HardwareDisplayController::UNASSOCIATED); - - // Until now the controller is just a stub. Initializing it will link it to a - // hardware display. - if (!InitializeControllerForPrimaryDisplay(drm_.get(), controller_.get())) { - LOG(ERROR) << "Failed to initialize controller"; - return gfx::kNullAcceleratedWidget; - } - - // Create a surface suitable for the current controller. - scoped_ptr<DriSurface> surface(CreateSurface(controller_.get())); - - if (!surface->Initialize()) { - LOG(ERROR) << "Failed to initialize surface"; - return gfx::kNullAcceleratedWidget; - } - - // Bind the surface to the controller. This will register the backing buffers - // with the hardware CRTC such that we can show the buffers. The controller - // takes ownership of the surface. - if (!controller_->BindSurfaceToController(surface.Pass())) { - LOG(ERROR) << "Failed to bind surface to controller"; - return gfx::kNullAcceleratedWidget; - } - - return reinterpret_cast<gfx::AcceleratedWidget>(controller_->get_surface()); -} - -bool DriSurfaceFactory::LoadEGLGLES2Bindings( - AddGLLibraryCallback add_gl_library, - SetGLGetProcAddressProcCallback set_gl_get_proc_address) { - return false; -} - -bool DriSurfaceFactory::AttemptToResizeAcceleratedWidget( - gfx::AcceleratedWidget w, - const gfx::Rect& bounds) { - return false; -} - -bool DriSurfaceFactory::SchedulePageFlip(gfx::AcceleratedWidget w) { - CHECK(state_ == INITIALIZED); - // TODO(dnicoara) Change this CHECK once we're running with the threaded - // compositor. - CHECK(base::MessageLoop::current()->type() == base::MessageLoop::TYPE_UI); - - // TODO(dnicoara) Once we can handle multiple displays this needs to be - // changed. - CHECK(w == kDefaultWidgetHandle); - - if (!controller_->SchedulePageFlip()) - return false; - - // Only wait for the page flip event to finish if it was properly scheduled. - // - // TODO(dnicoara) The following call will wait for the page flip event to - // complete. This means that it will block until the next VSync. Ideally the - // wait should happen in the message loop. The message loop would then - // schedule the next draw event. Alternatively, the VSyncProvider could be - // used to schedule the next draw. Unfortunately, at this point, - // DriOutputDevice does not provide any means to use any of the above - // solutions. Note that if the DRM callback does not schedule the next draw, - // then some sort of synchronization needs to take place since starting a new - // draw before the page flip happened is considered an error. However we can - // not use any lock constructs unless we're using the threaded compositor. - // Note that the following call does not use any locks, so it is safe to be - // made on the UI thread (thought not ideal). - WaitForPageFlipEvent(drm_->get_fd()); - - return true; -} - -SkCanvas* DriSurfaceFactory::GetCanvasForWidget( - gfx::AcceleratedWidget w) { - CHECK(state_ == INITIALIZED); - return reinterpret_cast<DriSurface*>(w)->GetDrawableForWidget(); -} - -gfx::VSyncProvider* DriSurfaceFactory::GetVSyncProvider( - gfx::AcceleratedWidget w) { - CHECK(state_ == INITIALIZED); - return new DriVSyncProvider(controller_.get()); -} - -//////////////////////////////////////////////////////////////////////////////// -// DriSurfaceFactory private - -DriSurface* DriSurfaceFactory::CreateSurface( - HardwareDisplayController* controller) { - return new DriSurface(controller); -} - -DriWrapper* DriSurfaceFactory::CreateWrapper() { - return new DriWrapper(kDefaultGraphicsCardPath); -} - -bool DriSurfaceFactory::InitializeControllerForPrimaryDisplay( - DriWrapper* drm, - HardwareDisplayController* controller) { - CHECK(state_ == SurfaceFactoryOzone::INITIALIZED); - - drmModeRes* resources = drmModeGetResources(drm->get_fd()); - - // Search for an active connector. - for (int i = 0; i < resources->count_connectors; ++i) { - drmModeConnector* connector = drmModeGetConnector( - drm->get_fd(), - resources->connectors[i]); - - if (!connector) - continue; - - if (connector->connection != DRM_MODE_CONNECTED || - connector->count_modes == 0) { - drmModeFreeConnector(connector); - continue; - } - - uint32_t crtc = GetCrtc(drm->get_fd(), resources, connector); - - if (!crtc) - continue; - - uint32_t dpms_property_id = GetDriProperty(drm->get_fd(), - connector, - kDPMSProperty); - - // TODO(dnicoara) Select one mode for now. In the future we may need to - // save all the modes and allow the user to choose a specific mode. Or - // even some fullscreen applications may need to change the mode. - controller->SetControllerInfo( - drm, - connector->connector_id, - crtc, - dpms_property_id, - connector->modes[0]); - - drmModeFreeConnector(connector); - - return true; - } - - return false; -} - -void DriSurfaceFactory::WaitForPageFlipEvent(int fd) { - drmEventContext drm_event; - drm_event.version = DRM_EVENT_CONTEXT_VERSION; - drm_event.page_flip_handler = HandlePageFlipEvent; - drm_event.vblank_handler = NULL; - - // Wait for the page-flip to complete. - drmHandleEvent(fd, &drm_event); -} - -} // namespace gfx diff --git a/chromium/ui/gfx/ozone/dri/dri_surface_factory.h b/chromium/ui/gfx/ozone/dri/dri_surface_factory.h deleted file mode 100644 index 616e989e910..00000000000 --- a/chromium/ui/gfx/ozone/dri/dri_surface_factory.h +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_GFX_OZONE_DRI_DRI_SURFACE_FACTORY_H_ -#define UI_GFX_OZONE_DRI_DRI_SURFACE_FACTORY_H_ - -#include "base/memory/scoped_ptr.h" -#include "ui/gfx/ozone/surface_factory_ozone.h" - -namespace gfx { - -class DriSurface; -class DriWrapper; -class HardwareDisplayController; - -// SurfaceFactoryOzone implementation on top of DRM/KMS using dumb buffers. -// This implementation is used in conjunction with the software rendering -// path. -class GFX_EXPORT DriSurfaceFactory : public SurfaceFactoryOzone { - public: - DriSurfaceFactory(); - virtual ~DriSurfaceFactory(); - - virtual HardwareState InitializeHardware() OVERRIDE; - virtual void ShutdownHardware() OVERRIDE; - - virtual gfx::AcceleratedWidget GetAcceleratedWidget() OVERRIDE; - virtual gfx::AcceleratedWidget RealizeAcceleratedWidget( - gfx::AcceleratedWidget w) OVERRIDE; - - virtual bool LoadEGLGLES2Bindings( - AddGLLibraryCallback add_gl_library, - SetGLGetProcAddressProcCallback set_gl_get_proc_address) OVERRIDE; - - virtual bool AttemptToResizeAcceleratedWidget( - gfx::AcceleratedWidget w, - const gfx::Rect& bounds) OVERRIDE; - - virtual bool SchedulePageFlip(gfx::AcceleratedWidget w) OVERRIDE; - - virtual SkCanvas* GetCanvasForWidget(gfx::AcceleratedWidget w) OVERRIDE; - - virtual gfx::VSyncProvider* GetVSyncProvider( - gfx::AcceleratedWidget w) OVERRIDE; - - private: - virtual DriSurface* CreateSurface( - HardwareDisplayController* controller); - - virtual DriWrapper* CreateWrapper(); - - virtual bool InitializeControllerForPrimaryDisplay( - DriWrapper* drm, - HardwareDisplayController* controller); - - // Blocks until a DRM event is read. - // TODO(dnicoara) Remove once we can safely move DRM event processing in the - // message loop while correctly signaling when we're done displaying the - // pending frame. - virtual void WaitForPageFlipEvent(int fd); - - scoped_ptr<DriWrapper> drm_; - - HardwareState state_; - - // Active output. - scoped_ptr<HardwareDisplayController> controller_; - - DISALLOW_COPY_AND_ASSIGN(DriSurfaceFactory); -}; - -} // namespace gfx - -#endif // UI_GFX_OZONE_DRI_DRI_SURFACE_FACTORY_H_ diff --git a/chromium/ui/gfx/ozone/dri/dri_surface_factory_unittest.cc b/chromium/ui/gfx/ozone/dri/dri_surface_factory_unittest.cc deleted file mode 100644 index 2e70f642f2a..00000000000 --- a/chromium/ui/gfx/ozone/dri/dri_surface_factory_unittest.cc +++ /dev/null @@ -1,292 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/memory/scoped_ptr.h" -#include "base/message_loop/message_loop.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "ui/gfx/ozone/dri/dri_skbitmap.h" -#include "ui/gfx/ozone/dri/dri_surface.h" -#include "ui/gfx/ozone/dri/dri_surface_factory.h" -#include "ui/gfx/ozone/dri/dri_wrapper.h" -#include "ui/gfx/ozone/dri/hardware_display_controller.h" -#include "ui/gfx/ozone/surface_factory_ozone.h" - -namespace { - -const drmModeModeInfo kDefaultMode = - {0, 6, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, {'\0'}}; - -// Mock file descriptor ID. -const int kFd = 3; - -// Mock connector ID. -const uint32_t kConnectorId = 1; - -// Mock CRTC ID. -const uint32_t kCrtcId = 1; - -const uint32_t kDPMSPropertyId = 1; - -const gfx::AcceleratedWidget kDefaultWidgetHandle = 1; - -// The real DriWrapper makes actual DRM calls which we can't use in unit tests. -class MockDriWrapper : public gfx::DriWrapper { - public: - MockDriWrapper(int fd) : DriWrapper(""), - add_framebuffer_expectation_(true), - page_flip_expectation_(true) { - fd_ = fd; - } - - virtual ~MockDriWrapper() { fd_ = -1; } - - virtual drmModeCrtc* GetCrtc(uint32_t crtc_id) OVERRIDE { - return new drmModeCrtc; - } - - virtual void FreeCrtc(drmModeCrtc* crtc) OVERRIDE { - delete crtc; - } - - virtual bool SetCrtc(uint32_t crtc_id, - uint32_t framebuffer, - uint32_t* connectors, - drmModeModeInfo* mode) OVERRIDE { - return true; - } - - virtual bool SetCrtc(drmModeCrtc* crtc, uint32_t* connectors) OVERRIDE { - return true; - } - - virtual bool AddFramebuffer(const drmModeModeInfo& mode, - uint8_t depth, - uint8_t bpp, - uint32_t stride, - uint32_t handle, - uint32_t* framebuffer) OVERRIDE { - return add_framebuffer_expectation_; - } - - virtual bool RemoveFramebuffer(uint32_t framebuffer) OVERRIDE { return true; } - - virtual bool PageFlip(uint32_t crtc_id, - uint32_t framebuffer, - void* data) OVERRIDE { - static_cast<gfx::HardwareDisplayController*>(data)->get_surface() - ->SwapBuffers(); - return page_flip_expectation_; - } - - virtual bool ConnectorSetProperty(uint32_t connector_id, - uint32_t property_id, - uint64_t value) OVERRIDE { return true; } - - void set_add_framebuffer_expectation(bool state) { - add_framebuffer_expectation_ = state; - } - - void set_page_flip_expectation(bool state) { - page_flip_expectation_ = state; - } - - private: - bool add_framebuffer_expectation_; - bool page_flip_expectation_; - - DISALLOW_COPY_AND_ASSIGN(MockDriWrapper); -}; - -class MockDriSkBitmap : public gfx::DriSkBitmap { - public: - MockDriSkBitmap() : DriSkBitmap(kFd) {} - virtual ~MockDriSkBitmap() {} - - virtual bool Initialize() OVERRIDE { - allocPixels(); - eraseColor(SK_ColorBLACK); - return true; - } - - private: - DISALLOW_COPY_AND_ASSIGN(MockDriSkBitmap); -}; - -class MockDriSurface : public gfx::DriSurface { - public: - MockDriSurface(gfx::HardwareDisplayController* controller) - : DriSurface(controller) {} - virtual ~MockDriSurface() {} - - private: - virtual gfx::DriSkBitmap* CreateBuffer() OVERRIDE { - return new MockDriSkBitmap(); - } - - DISALLOW_COPY_AND_ASSIGN(MockDriSurface); -}; - -// SSFO would normally allocate DRM resources. We can't rely on having a DRM -// backend to allocate and display our buffers. Thus, we replace these -// resources with stubs. For DRM calls, we simply use stubs that do nothing and -// for buffers we use the default SkBitmap allocator. -class MockDriSurfaceFactory - : public gfx::DriSurfaceFactory { - public: - MockDriSurfaceFactory() - : DriSurfaceFactory(), - mock_drm_(NULL), - drm_wrapper_expectation_(true), - initialize_controller_expectation_(true) {} - virtual ~MockDriSurfaceFactory() {}; - - void set_drm_wrapper_expectation(bool state) { - drm_wrapper_expectation_ = state; - } - - void set_initialize_controller_expectation(bool state) { - initialize_controller_expectation_ = state; - } - - MockDriWrapper* get_drm() const { - return mock_drm_; - } - - private: - virtual gfx::DriSurface* CreateSurface( - gfx::HardwareDisplayController* controller) OVERRIDE { - return new MockDriSurface(controller); - } - - virtual gfx::DriWrapper* CreateWrapper() OVERRIDE { - if (drm_wrapper_expectation_) - mock_drm_ = new MockDriWrapper(kFd); - else - mock_drm_ = new MockDriWrapper(-1); - - return mock_drm_; - } - - // Normally we'd use DRM to figure out the controller configuration. But we - // can't use DRM in unit tests, so we just create a fake configuration. - virtual bool InitializeControllerForPrimaryDisplay( - gfx::DriWrapper* drm, - gfx::HardwareDisplayController* controller) OVERRIDE { - if (initialize_controller_expectation_) { - controller->SetControllerInfo(drm, - kConnectorId, - kCrtcId, - kDPMSPropertyId, - kDefaultMode); - return true; - } else { - return false; - } - } - - virtual void WaitForPageFlipEvent(int fd) OVERRIDE {} - - MockDriWrapper* mock_drm_; - bool drm_wrapper_expectation_; - bool initialize_controller_expectation_; - - DISALLOW_COPY_AND_ASSIGN(MockDriSurfaceFactory); -}; - -} // namespace - -class DriSurfaceFactoryTest : public testing::Test { - public: - DriSurfaceFactoryTest() {} - - virtual void SetUp() OVERRIDE; - virtual void TearDown() OVERRIDE; - protected: - scoped_ptr<base::MessageLoop> message_loop_; - scoped_ptr<MockDriSurfaceFactory> factory_; - - private: - DISALLOW_COPY_AND_ASSIGN(DriSurfaceFactoryTest); -}; - -void DriSurfaceFactoryTest::SetUp() { - message_loop_.reset(new base::MessageLoop(base::MessageLoop::TYPE_UI)); - factory_.reset(new MockDriSurfaceFactory()); -} - -void DriSurfaceFactoryTest::TearDown() { - factory_.reset(); - message_loop_.reset(); -} - -TEST_F(DriSurfaceFactoryTest, FailInitialization) { - factory_->set_drm_wrapper_expectation(false); - - EXPECT_EQ(gfx::SurfaceFactoryOzone::FAILED, factory_->InitializeHardware()); -} - -TEST_F(DriSurfaceFactoryTest, SuccessfulInitialization) { - EXPECT_EQ(gfx::SurfaceFactoryOzone::INITIALIZED, - factory_->InitializeHardware()); -} - -TEST_F(DriSurfaceFactoryTest, FailSurfaceInitialization) { - factory_->set_initialize_controller_expectation(false); - - EXPECT_EQ(gfx::SurfaceFactoryOzone::INITIALIZED, - factory_->InitializeHardware()); - - gfx::AcceleratedWidget w = factory_->GetAcceleratedWidget(); - EXPECT_EQ(kDefaultWidgetHandle, w); - - EXPECT_EQ(gfx::kNullAcceleratedWidget, factory_->RealizeAcceleratedWidget(w)); -} - -TEST_F(DriSurfaceFactoryTest, FailBindingSurfaceToController) { - EXPECT_EQ(gfx::SurfaceFactoryOzone::INITIALIZED, - factory_->InitializeHardware()); - - factory_->get_drm()->set_add_framebuffer_expectation(false); - - gfx::AcceleratedWidget w = factory_->GetAcceleratedWidget(); - EXPECT_EQ(kDefaultWidgetHandle, w); - - EXPECT_EQ(gfx::kNullAcceleratedWidget, factory_->RealizeAcceleratedWidget(w)); -} - -TEST_F(DriSurfaceFactoryTest, SuccessfulWidgetRealization) { - EXPECT_EQ(gfx::SurfaceFactoryOzone::INITIALIZED, - factory_->InitializeHardware()); - - gfx::AcceleratedWidget w = factory_->GetAcceleratedWidget(); - EXPECT_EQ(kDefaultWidgetHandle, w); - - EXPECT_NE(gfx::kNullAcceleratedWidget, factory_->RealizeAcceleratedWidget(w)); -} - -TEST_F(DriSurfaceFactoryTest, FailSchedulePageFlip) { - EXPECT_EQ(gfx::SurfaceFactoryOzone::INITIALIZED, - factory_->InitializeHardware()); - - factory_->get_drm()->set_page_flip_expectation(false); - - gfx::AcceleratedWidget w = factory_->GetAcceleratedWidget(); - EXPECT_EQ(kDefaultWidgetHandle, w); - - EXPECT_NE(gfx::kNullAcceleratedWidget, factory_->RealizeAcceleratedWidget(w)); - - EXPECT_FALSE(factory_->SchedulePageFlip(w)); -} - -TEST_F(DriSurfaceFactoryTest, SuccessfulSchedulePageFlip) { - EXPECT_EQ(gfx::SurfaceFactoryOzone::INITIALIZED, - factory_->InitializeHardware()); - - gfx::AcceleratedWidget w = factory_->GetAcceleratedWidget(); - EXPECT_EQ(kDefaultWidgetHandle, w); - - EXPECT_NE(gfx::kNullAcceleratedWidget, factory_->RealizeAcceleratedWidget(w)); - - EXPECT_TRUE(factory_->SchedulePageFlip(w)); -} diff --git a/chromium/ui/gfx/ozone/dri/dri_surface_unittest.cc b/chromium/ui/gfx/ozone/dri/dri_surface_unittest.cc deleted file mode 100644 index 754659163a2..00000000000 --- a/chromium/ui/gfx/ozone/dri/dri_surface_unittest.cc +++ /dev/null @@ -1,212 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "testing/gtest/include/gtest/gtest.h" -#include "third_party/skia/include/core/SkCanvas.h" -#include "third_party/skia/include/core/SkColor.h" -#include "third_party/skia/include/core/SkDevice.h" -#include "ui/gfx/ozone/dri/dri_skbitmap.h" -#include "ui/gfx/ozone/dri/dri_surface.h" -#include "ui/gfx/ozone/dri/hardware_display_controller.h" - -namespace { - -// Create a basic mode for a 6x4 screen. -const drmModeModeInfo kDefaultMode = - {0, 6, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, {'\0'}}; - -// Mock file descriptor ID. -const int kFd = 3; - -// Mock connector ID. -const uint32_t kConnectorId = 1; - -// Mock CRTC ID. -const uint32_t kCrtcId = 1; - -// Mock DPMS property ID. -const uint32_t kDPMSPropertyId = 1; - -class MockDriWrapper : public gfx::DriWrapper { - public: - MockDriWrapper() : DriWrapper(""), id_(1) { fd_ = kFd; } - virtual ~MockDriWrapper() { fd_ = -1; } - - virtual drmModeCrtc* GetCrtc(uint32_t crtc_id) OVERRIDE { return NULL; } - virtual void FreeCrtc(drmModeCrtc* crtc) OVERRIDE {} - virtual bool SetCrtc(uint32_t crtc_id, - uint32_t framebuffer, - uint32_t* connectors, - drmModeModeInfo* mode) OVERRIDE { return true; } - virtual bool SetCrtc(drmModeCrtc* crtc, uint32_t* connectors) OVERRIDE { - return true; - } - virtual bool AddFramebuffer(const drmModeModeInfo& mode, - uint8_t depth, - uint8_t bpp, - uint32_t stride, - uint32_t handle, - uint32_t* framebuffer) OVERRIDE { - *framebuffer = id_++; - return true; - } - virtual bool RemoveFramebuffer(uint32_t framebuffer) OVERRIDE { return true; } - virtual bool PageFlip(uint32_t crtc_id, - uint32_t framebuffer, - void* data) OVERRIDE { - return true; - } - virtual bool ConnectorSetProperty(uint32_t connector_id, - uint32_t property_id, - uint64_t value) OVERRIDE { return true; } - - private: - int id_; - DISALLOW_COPY_AND_ASSIGN(MockDriWrapper); -}; - -class MockDriSkBitmap : public gfx::DriSkBitmap { - public: - MockDriSkBitmap(int fd, - bool initialize_expectation) - : DriSkBitmap(fd), - initialize_expectation_(initialize_expectation) {} - virtual ~MockDriSkBitmap() {} - - virtual bool Initialize() OVERRIDE { - if (!initialize_expectation_) - return false; - - allocPixels(); - // Clear the bitmap to black. - eraseColor(SK_ColorBLACK); - - return true; - } - private: - bool initialize_expectation_; - - DISALLOW_COPY_AND_ASSIGN(MockDriSkBitmap); -}; - -class MockDriSurface : public gfx::DriSurface { - public: - MockDriSurface(gfx::HardwareDisplayController* controller) - : DriSurface(controller), - initialize_expectation_(true) {} - virtual ~MockDriSurface() {} - - void set_initialize_expectation(bool state) { - initialize_expectation_ = state; - } - - private: - virtual gfx::DriSkBitmap* CreateBuffer() OVERRIDE { - return new MockDriSkBitmap(kFd, initialize_expectation_); - } - - bool initialize_expectation_; - - DISALLOW_COPY_AND_ASSIGN(MockDriSurface); -}; - -} // namespace - -class DriSurfaceTest : public testing::Test { - public: - DriSurfaceTest() {} - - virtual void SetUp() OVERRIDE; - virtual void TearDown() OVERRIDE; - - protected: - scoped_ptr<MockDriWrapper> drm_; - scoped_ptr<gfx::HardwareDisplayController> controller_; - scoped_ptr<MockDriSurface> surface_; - - private: - DISALLOW_COPY_AND_ASSIGN(DriSurfaceTest); -}; - -void DriSurfaceTest::SetUp() { - drm_.reset(new MockDriWrapper()); - controller_.reset(new gfx::HardwareDisplayController()); - controller_->SetControllerInfo( - drm_.get(), kConnectorId, kCrtcId, kDPMSPropertyId, kDefaultMode); - - surface_.reset(new MockDriSurface(controller_.get())); -} - -void DriSurfaceTest::TearDown() { - surface_.reset(); - controller_.reset(); - drm_.reset(); -} - -TEST_F(DriSurfaceTest, FailInitialization) { - surface_->set_initialize_expectation(false); - EXPECT_FALSE(surface_->Initialize()); -} - -TEST_F(DriSurfaceTest, SuccessfulInitialization) { - EXPECT_TRUE(surface_->Initialize()); -} - -TEST_F(DriSurfaceTest, CheckFBIDOnSwap) { - EXPECT_TRUE(surface_->Initialize()); - controller_->BindSurfaceToController( - surface_.PassAs<gfx::DriSurface>()); - - // Check that the framebuffer ID is correct. - EXPECT_EQ(2u, controller_->get_surface()->GetFramebufferId()); - - controller_->get_surface()->SwapBuffers(); - - EXPECT_EQ(1u, controller_->get_surface()->GetFramebufferId()); -} - -TEST_F(DriSurfaceTest, CheckPixelPointerOnSwap) { - EXPECT_TRUE(surface_->Initialize()); - - void* bitmap_pixels1 = surface_->GetDrawableForWidget()->getDevice() - ->accessBitmap(false).getPixels(); - - surface_->SwapBuffers(); - - void* bitmap_pixels2 = surface_->GetDrawableForWidget()->getDevice() - ->accessBitmap(false).getPixels(); - - // Check that once the buffers have been swapped the drawable's underlying - // pixels have been changed. - EXPECT_NE(bitmap_pixels1, bitmap_pixels2); -} - -TEST_F(DriSurfaceTest, CheckCorrectBufferSync) { - EXPECT_TRUE(surface_->Initialize()); - - SkCanvas* canvas = surface_->GetDrawableForWidget(); - SkRect clip; - // Modify part of the canvas. - clip.set(0, 0, - canvas->getDeviceSize().width() / 2, - canvas->getDeviceSize().height() / 2); - canvas->clipRect(clip, SkRegion::kReplace_Op); - - canvas->drawColor(SK_ColorWHITE); - - surface_->SwapBuffers(); - - // Verify that the modified contents have been copied over on swap (make sure - // the 2 buffers have the same content). - for (int i = 0; i < canvas->getDeviceSize().height(); ++i) { - for (int j = 0; j < canvas->getDeviceSize().width(); ++j) { - if (i < clip.height() && j < clip.width()) - EXPECT_EQ(SK_ColorWHITE, - canvas->getDevice()->accessBitmap(false).getColor(j, i)); - else - EXPECT_EQ(SK_ColorBLACK, - canvas->getDevice()->accessBitmap(false).getColor(j, i)); - } - } -} diff --git a/chromium/ui/gfx/ozone/dri/dri_vsync_provider.cc b/chromium/ui/gfx/ozone/dri/dri_vsync_provider.cc deleted file mode 100644 index b5717e686f1..00000000000 --- a/chromium/ui/gfx/ozone/dri/dri_vsync_provider.cc +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/gfx/ozone/dri/dri_vsync_provider.h" - -#include "base/time/time.h" -#include "ui/gfx/ozone/dri/hardware_display_controller.h" - -namespace gfx { - -DriVSyncProvider::DriVSyncProvider(HardwareDisplayController* controller) - : controller_(controller) {} - -DriVSyncProvider::~DriVSyncProvider() {} - -void DriVSyncProvider::GetVSyncParameters(const UpdateVSyncCallback& callback) { - // The value is invalid, so we can't update the parameters. - if (controller_->get_time_of_last_flip() == 0) - return; - - // Stores the time of the last refresh. - base::TimeTicks timebase = - base::TimeTicks::FromInternalValue(controller_->get_time_of_last_flip()); - // Stores the refresh rate. - base::TimeDelta interval = - base::TimeDelta::FromSeconds(1) / controller_->get_mode().vrefresh; - - callback.Run(timebase, interval); -} - -} // namespace gfx diff --git a/chromium/ui/gfx/ozone/dri/dri_vsync_provider.h b/chromium/ui/gfx/ozone/dri/dri_vsync_provider.h deleted file mode 100644 index b6cb6047eb8..00000000000 --- a/chromium/ui/gfx/ozone/dri/dri_vsync_provider.h +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_GFX_OZONE_IMPL_DRI_VSYNC_PROVIDER_H_ -#define UI_GFX_OZONE_IMPL_DRI_VSYNC_PROVIDER_H_ - -#include "ui/gfx/vsync_provider.h" - -namespace gfx { - -class HardwareDisplayController; - -class DriVSyncProvider : public VSyncProvider { - public: - DriVSyncProvider(HardwareDisplayController* controller); - virtual ~DriVSyncProvider(); - - virtual void GetVSyncParameters(const UpdateVSyncCallback& callback) OVERRIDE; - - private: - HardwareDisplayController* controller_; - - DISALLOW_COPY_AND_ASSIGN(DriVSyncProvider); -}; - -} // namespace gfx - -#endif // UI_GFX_OZONE_IMPL_DRI_VSYNC_PROVIDER_H_ diff --git a/chromium/ui/gfx/ozone/dri/dri_wrapper.cc b/chromium/ui/gfx/ozone/dri/dri_wrapper.cc deleted file mode 100644 index 2b1104b444d..00000000000 --- a/chromium/ui/gfx/ozone/dri/dri_wrapper.cc +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/gfx/ozone/dri/dri_wrapper.h" - -#include <fcntl.h> -#include <unistd.h> -#include <xf86drmMode.h> - -#include "base/logging.h" - -namespace gfx { - -DriWrapper::DriWrapper(const char* device_path) { - fd_ = open(device_path, O_RDWR | O_CLOEXEC); -} - -DriWrapper::~DriWrapper() { - if (fd_ >= 0) - close(fd_); -} - -drmModeCrtc* DriWrapper::GetCrtc(uint32_t crtc_id) { - CHECK(fd_ >= 0); - return drmModeGetCrtc(fd_, crtc_id); -} - -void DriWrapper::FreeCrtc(drmModeCrtc* crtc) { - drmModeFreeCrtc(crtc); -} - -bool DriWrapper::SetCrtc(uint32_t crtc_id, - uint32_t framebuffer, - uint32_t* connectors, - drmModeModeInfo* mode) { - CHECK(fd_ >= 0); - return !drmModeSetCrtc(fd_, crtc_id, framebuffer, 0, 0, connectors, 1, mode); -} - -bool DriWrapper::SetCrtc(drmModeCrtc* crtc, uint32_t* connectors) { - CHECK(fd_ >= 0); - return !drmModeSetCrtc(fd_, - crtc->crtc_id, - crtc->buffer_id, - crtc->x, - crtc->y, - connectors, - 1, - &crtc->mode); -} - -bool DriWrapper::AddFramebuffer(const drmModeModeInfo& mode, - uint8_t depth, - uint8_t bpp, - uint32_t stride, - uint32_t handle, - uint32_t* framebuffer) { - CHECK(fd_ >= 0); - return !drmModeAddFB(fd_, - mode.hdisplay, - mode.vdisplay, - depth, - bpp, - stride, - handle, - framebuffer); -} - -bool DriWrapper::RemoveFramebuffer(uint32_t framebuffer) { - CHECK(fd_ >= 0); - return !drmModeRmFB(fd_, framebuffer); -} - -bool DriWrapper::PageFlip(uint32_t crtc_id, - uint32_t framebuffer, - void* data) { - CHECK(fd_ >= 0); - return !drmModePageFlip(fd_, - crtc_id, - framebuffer, - DRM_MODE_PAGE_FLIP_EVENT, - data); -} - -bool DriWrapper::ConnectorSetProperty(uint32_t connector_id, - uint32_t property_id, - uint64_t value) { - CHECK(fd_ >= 0); - return !drmModeConnectorSetProperty(fd_, connector_id, property_id, value); -} - -} // namespace gfx diff --git a/chromium/ui/gfx/ozone/dri/dri_wrapper.h b/chromium/ui/gfx/ozone/dri/dri_wrapper.h deleted file mode 100644 index 0848132c9ad..00000000000 --- a/chromium/ui/gfx/ozone/dri/dri_wrapper.h +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_GFX_OZONE_DRI_DRI_WRAPPER_H_ -#define UI_GFX_OZONE_DRI_DRI_WRAPPER_H_ - -#include <stdint.h> - -#include "base/basictypes.h" -#include "ui/gfx/gfx_export.h" - -typedef struct _drmModeCrtc drmModeCrtc; -typedef struct _drmModeModeInfo drmModeModeInfo; - -namespace gfx { - -// Wraps DRM calls into a nice interface. Used to provide different -// implementations of the DRM calls. For the actual implementation the DRM API -// would be called. In unit tests this interface would be stubbed. -class GFX_EXPORT DriWrapper { - public: - DriWrapper(const char* device_path); - virtual ~DriWrapper(); - - // Get the CRTC state. This is generally used to save state before using the - // CRTC. When the user finishes using the CRTC, the user should restore the - // CRTC to it's initial state. Use |SetCrtc| to restore the state. - virtual drmModeCrtc* GetCrtc(uint32_t crtc_id); - - // Frees the CRTC mode object. - virtual void FreeCrtc(drmModeCrtc* crtc); - - // Used to configure CRTC with ID |crtc_id| to use the connector in - // |connectors|. The CRTC will be configured with mode |mode| and will display - // the framebuffer with ID |framebuffer|. Before being able to display the - // framebuffer, it should be registered with the CRTC using |AddFramebuffer|. - virtual bool SetCrtc(uint32_t crtc_id, - uint32_t framebuffer, - uint32_t* connectors, - drmModeModeInfo* mode); - - // Used to set a specific configuration to the CRTC. Normally this function - // would be called with a CRTC saved state (from |GetCrtc|) to restore it to - // its original configuration. - virtual bool SetCrtc(drmModeCrtc* crtc, uint32_t* connectors); - - // Register a buffer with the CRTC. On successful registration, the CRTC will - // assign a framebuffer ID to |framebuffer|. - virtual bool AddFramebuffer(const drmModeModeInfo& mode, - uint8_t depth, - uint8_t bpp, - uint32_t stride, - uint32_t handle, - uint32_t* framebuffer); - - // Deregister the given |framebuffer|. - virtual bool RemoveFramebuffer(uint32_t framebuffer); - - // Schedules a pageflip for CRTC |crtc_id|. This function will return - // immediately. Upon completion of the pageflip event, the CRTC will be - // displaying the buffer with ID |framebuffer| and will have a DRM event - // queued on |fd_|. |data| is a generic pointer to some information the user - // will receive when processing the pageflip event. - virtual bool PageFlip(uint32_t crtc_id, uint32_t framebuffer, void* data); - - // Sets the value of property with ID |property_id| to |value|. The property - // is applied to the connector with ID |connector_id|. - virtual bool ConnectorSetProperty(uint32_t connector_id, - uint32_t property_id, - uint64_t value); - - int get_fd() const { return fd_; } - - protected: - // The file descriptor associated with this wrapper. All DRM operations will - // be performed using this FD. - int fd_; - - private: - DISALLOW_COPY_AND_ASSIGN(DriWrapper); -}; - -} // namespace gfx - -#endif // UI_GFX_OZONE_DRI_DRI_WRAPPER_H_ diff --git a/chromium/ui/gfx/ozone/dri/hardware_display_controller.cc b/chromium/ui/gfx/ozone/dri/hardware_display_controller.cc deleted file mode 100644 index 6accfc00d53..00000000000 --- a/chromium/ui/gfx/ozone/dri/hardware_display_controller.cc +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/gfx/ozone/dri/hardware_display_controller.h" - -#include <errno.h> -#include <string.h> - -#include "base/basictypes.h" -#include "base/logging.h" -#include "base/time/time.h" -#include "ui/gfx/ozone/dri/dri_skbitmap.h" -#include "ui/gfx/ozone/dri/dri_surface.h" -#include "ui/gfx/ozone/dri/dri_wrapper.h" - -namespace gfx { - -HardwareDisplayController::HardwareDisplayController() - : drm_(NULL), - connector_id_(0), - crtc_id_(0), - mode_(), - saved_crtc_(NULL), - state_(UNASSOCIATED), - surface_(), - time_of_last_flip_(0) {} - -void HardwareDisplayController::SetControllerInfo( - DriWrapper* drm, - uint32_t connector_id, - uint32_t crtc_id, - uint32_t dpms_property_id, - drmModeModeInfo mode) { - drm_ = drm; - connector_id_ = connector_id; - crtc_id_ = crtc_id; - dpms_property_id_ = dpms_property_id; - mode_ = mode; - saved_crtc_ = drm_->GetCrtc(crtc_id_); - state_ = UNINITIALIZED; -} - -HardwareDisplayController::~HardwareDisplayController() { - if (saved_crtc_) { - if (!drm_->SetCrtc(saved_crtc_, &connector_id_)) - DLOG(ERROR) << "Failed to restore CRTC state: " << strerror(errno); - drm_->FreeCrtc(saved_crtc_); - } - - if (surface_.get()) { - // Unregister the buffers. - for (int i = 0; i < 2; ++i) { - if (!drm_->RemoveFramebuffer(surface_->bitmaps_[i]->get_framebuffer())) - DLOG(ERROR) << "Failed to remove FB: " << strerror(errno); - } - } -} - -bool -HardwareDisplayController::BindSurfaceToController( - scoped_ptr<DriSurface> surface) { - CHECK(state_ == UNINITIALIZED); - - // Register the buffers. - for (int i = 0; i < 2; ++i) { - uint32_t fb_id; - if (!drm_->AddFramebuffer(mode_, - surface->bitmaps_[i]->GetColorDepth(), - surface->bitmaps_[i]->bytesPerPixel() << 3, - surface->bitmaps_[i]->rowBytes(), - surface->bitmaps_[i]->get_handle(), - &fb_id)) { - DLOG(ERROR) << "Failed to register framebuffer: " << strerror(errno); - state_ = FAILED; - return false; - } - surface->bitmaps_[i]->set_framebuffer(fb_id); - } - - surface_.reset(surface.release()); - state_ = SURFACE_INITIALIZED; - return true; -} - -bool HardwareDisplayController::SchedulePageFlip() { - CHECK(state_ == SURFACE_INITIALIZED || state_ == INITIALIZED); - - if (state_ == SURFACE_INITIALIZED) { - // Perform the initial modeset. - if (!drm_->SetCrtc(crtc_id_, - surface_->GetFramebufferId(), - &connector_id_, - &mode_)) { - DLOG(ERROR) << "Cannot set CRTC: " << strerror(errno); - state_ = FAILED; - return false; - } else { - state_ = INITIALIZED; - } - - if (dpms_property_id_) - drm_->ConnectorSetProperty(connector_id_, - dpms_property_id_, - DRM_MODE_DPMS_ON); - } - - if (!drm_->PageFlip(crtc_id_, - surface_->GetFramebufferId(), - this)) { - state_ = FAILED; - LOG(ERROR) << "Cannot page flip: " << strerror(errno); - return false; - } - - return true; -} - -void HardwareDisplayController::OnPageFlipEvent(unsigned int frame, - unsigned int seconds, - unsigned int useconds) { - time_of_last_flip_ = - static_cast<uint64_t>(seconds) * base::Time::kMicrosecondsPerSecond + - useconds; - - surface_->SwapBuffers(); -} - -} // namespace gfx diff --git a/chromium/ui/gfx/ozone/dri/hardware_display_controller.h b/chromium/ui/gfx/ozone/dri/hardware_display_controller.h deleted file mode 100644 index cae8a6ba821..00000000000 --- a/chromium/ui/gfx/ozone/dri/hardware_display_controller.h +++ /dev/null @@ -1,190 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_GFX_OZONE_DRI_HARDWARE_DISPLAY_CONTROLLER_H_ -#define UI_GFX_OZONE_DRI_HARDWARE_DISPLAY_CONTROLLER_H_ - -#include <stddef.h> -#include <stdint.h> -#include <xf86drmMode.h> -#include <vector> - -#include "base/basictypes.h" -#include "base/memory/scoped_ptr.h" -#include "ui/gfx/gfx_export.h" -#include "ui/gfx/ozone/dri/dri_wrapper.h" - -namespace gfx { - -class DriSurface; - -// The HDCOz will handle modesettings and scannout operations for hardware -// devices. -// -// In the DRM world there are 3 components that need to be paired up to be able -// to display an image to the monitor: CRTC (cathode ray tube controller), -// encoder and connector. The CRTC determines which framebuffer to read, when -// to scanout and where to scanout. Encoders converts the stream from the CRTC -// to the appropriate format for the connector. The connector is the physical -// connection that monitors connect to. -// -// There is no 1:1:1 pairing for these components. It is possible for an encoder -// to be compatible to multiple CRTCs and each connector can be used with -// multiple encoders. In addition, it is possible to use one CRTC with multiple -// connectors such that we can display the same image on multiple monitors. -// -// For example, the following configuration shows 2 different screens being -// initialized separately. -// ------------- ------------- -// | Connector | | Connector | -// | HDMI | | VGA | -// ------------- ------------- -// ^ ^ -// | | -// ------------- ------------- -// | Encoder1 | | Encoder2 | -// ------------- ------------- -// ^ ^ -// | | -// ------------- ------------- -// | CRTC1 | | CRTC2 | -// ------------- ------------- -// -// In the following configuration 2 different screens are associated with the -// same CRTC, so on scanout the same framebuffer will be displayed on both -// monitors. -// ------------- ------------- -// | Connector | | Connector | -// | HDMI | | VGA | -// ------------- ------------- -// ^ ^ -// | | -// ------------- ------------- -// | Encoder1 | | Encoder2 | -// ------------- ------------- -// ^ ^ -// | | -// ---------------------- -// | CRTC1 | -// ---------------------- -// -// Note that it is possible to have more connectors than CRTCs which means that -// only a subset of connectors can be active independently, showing different -// framebuffers. Though, in this case, it would be possible to have all -// connectors active if some use the same CRTC to mirror the display. -// -// TODO(dnicoara) Need to have a way to detect events (such as monitor -// connected or disconnected). -class GFX_EXPORT HardwareDisplayController { - public: - // Controller states. The state transitions will happen from top to bottom. - enum State { - // When we allocate a HDCO as a stub. At this point there is no connector - // and CRTC associated with this device. - UNASSOCIATED, - - // When |SetControllerInfo| is called and the HDCO has the information of - // the hardware it will control. At this point it knows everything it needs - // to control the hardware but doesn't have a surface. - UNINITIALIZED, - - // A surface is associated with the HDCO. This means that the controller can - // potentially display the backing surface to the display. Though the - // surface framebuffer still needs to be registered with the CRTC. - SURFACE_INITIALIZED, - - // The CRTC now knows about the surface attributes. - INITIALIZED, - - // Error state if any of the initialization steps fail. - FAILED, - }; - - HardwareDisplayController(); - - ~HardwareDisplayController(); - - // Set the hardware configuration for this HDCO. Once this is set, the HDCO is - // responsible for keeping track of the connector and CRTC and cleaning up - // when it is destroyed. - void SetControllerInfo(DriWrapper* drm, - uint32_t connector_id, - uint32_t crtc_id, - uint32_t dpms_property_id, - drmModeModeInfo mode); - - // Associate the HDCO with a surface implementation and initialize it. - bool BindSurfaceToController(scoped_ptr<DriSurface> surface); - - // Schedules the |surface_|'s framebuffer to be displayed on the next vsync - // event. The event will be posted on the graphics card file descriptor |fd_| - // and it can be read and processed by |drmHandleEvent|. That function can - // define the callback for the page flip event. A generic data argument will - // be presented to the callback. We use that argument to pass in the HDCO - // object the event belongs to. - // - // Between this call and the callback, the framebuffer used in this call - // should not be modified in any way as it would cause screen tearing if the - // hardware performed the flip. Note that the frontbuffer should also not - // be modified as it could still be displayed. - // - // Note that this function does not block. Also, this function should not be - // called again before the page flip occurrs. - // - // Returns true if the page flip was successfully registered, false otherwise. - bool SchedulePageFlip(); - - // Called when the page flip event occurred. The event is provided by the - // kernel when a VBlank event finished. This allows the controller to - // update internal state and propagate the update to the surface. - // The tuple (seconds, useconds) represents the event timestamp. |seconds| - // represents the number of seconds while |useconds| represents the - // microseconds (< 1 second) in the timestamp. - void OnPageFlipEvent(unsigned int frame, - unsigned int seconds, - unsigned int useconds); - - State get_state() const { return state_; }; - - int get_fd() const { return drm_->get_fd(); }; - - const drmModeModeInfo& get_mode() const { return mode_; }; - - DriSurface* get_surface() const { return surface_.get(); }; - - uint64_t get_time_of_last_flip() const { - return time_of_last_flip_; - }; - - private: - // Object containing the connection to the graphics device and wraps the API - // calls to control it. - DriWrapper* drm_; - - // TODO(dnicoara) Need to allow a CRTC to have multiple connectors. - uint32_t connector_id_; - - uint32_t crtc_id_; - - uint32_t dpms_property_id_; - - // TODO(dnicoara) Need to store all the modes. - drmModeModeInfo mode_; - - // Saved CRTC state from before we used it. Need it to restore state once we - // are finished using this device. - drmModeCrtc* saved_crtc_; - - State state_; - - scoped_ptr<DriSurface> surface_; - - uint64_t time_of_last_flip_; - - DISALLOW_COPY_AND_ASSIGN(HardwareDisplayController); -}; - -} // namespace gfx - -#endif // UI_GFX_OZONE_DRI_HARDWARE_DISPLAY_CONTROLLER_H_ diff --git a/chromium/ui/gfx/ozone/dri/hardware_display_controller_unittest.cc b/chromium/ui/gfx/ozone/dri/hardware_display_controller_unittest.cc deleted file mode 100644 index 3806b5a157b..00000000000 --- a/chromium/ui/gfx/ozone/dri/hardware_display_controller_unittest.cc +++ /dev/null @@ -1,301 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "testing/gtest/include/gtest/gtest.h" -#include "ui/gfx/ozone/dri/dri_skbitmap.h" -#include "ui/gfx/ozone/dri/dri_surface.h" -#include "ui/gfx/ozone/dri/dri_wrapper.h" -#include "ui/gfx/ozone/dri/hardware_display_controller.h" - -namespace { - -// Create a basic mode for a 6x4 screen. -const drmModeModeInfo kDefaultMode = - {0, 6, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, {'\0'}}; - -// Mock file descriptor ID. -const int kFd = 3; - -// Mock connector ID. -const uint32_t kConnectorId = 1; - -// Mock CRTC ID. -const uint32_t kCrtcId = 1; - -const uint32_t kDPMSPropertyId = 1; - -// The real DriWrapper makes actual DRM calls which we can't use in unit tests. -class MockDriWrapper : public gfx::DriWrapper { - public: - MockDriWrapper(int fd) : DriWrapper(""), - get_crtc_call_count_(0), - free_crtc_call_count_(0), - restore_crtc_call_count_(0), - add_framebuffer_call_count_(0), - remove_framebuffer_call_count_(0), - set_crtc_expectation_(true), - add_framebuffer_expectation_(true), - page_flip_expectation_(true) { - fd_ = fd; - } - - virtual ~MockDriWrapper() { fd_ = -1; } - - virtual drmModeCrtc* GetCrtc(uint32_t crtc_id) OVERRIDE { - get_crtc_call_count_++; - return new drmModeCrtc; - } - - virtual void FreeCrtc(drmModeCrtc* crtc) OVERRIDE { - free_crtc_call_count_++; - delete crtc; - } - - virtual bool SetCrtc(uint32_t crtc_id, - uint32_t framebuffer, - uint32_t* connectors, - drmModeModeInfo* mode) OVERRIDE { - return set_crtc_expectation_; - } - - virtual bool SetCrtc(drmModeCrtc* crtc, uint32_t* connectors) OVERRIDE { - restore_crtc_call_count_++; - return true; - } - - virtual bool AddFramebuffer(const drmModeModeInfo& mode, - uint8_t depth, - uint8_t bpp, - uint32_t stride, - uint32_t handle, - uint32_t* framebuffer) OVERRIDE { - add_framebuffer_call_count_++; - return add_framebuffer_expectation_; - } - - virtual bool RemoveFramebuffer(uint32_t framebuffer) OVERRIDE { - remove_framebuffer_call_count_++; - return true; - } - - virtual bool PageFlip(uint32_t crtc_id, - uint32_t framebuffer, - void* data) OVERRIDE { - return page_flip_expectation_; - } - - virtual bool ConnectorSetProperty(uint32_t connector_id, - uint32_t property_id, - uint64_t value) OVERRIDE { return true; } - - int get_get_crtc_call_count() const { - return get_crtc_call_count_; - } - - int get_free_crtc_call_count() const { - return free_crtc_call_count_; - } - - int get_restore_crtc_call_count() const { - return restore_crtc_call_count_; - } - - int get_add_framebuffer_call_count() const { - return add_framebuffer_call_count_; - } - - int get_remove_framebuffer_call_count() const { - return remove_framebuffer_call_count_; - } - - void set_set_crtc_expectation(bool state) { - set_crtc_expectation_ = state; - } - - void set_add_framebuffer_expectation(bool state) { - add_framebuffer_expectation_ = state; - } - - void set_page_flip_expectation(bool state) { - page_flip_expectation_ = state; - } - - private: - int get_crtc_call_count_; - int free_crtc_call_count_; - int restore_crtc_call_count_; - int add_framebuffer_call_count_; - int remove_framebuffer_call_count_; - - bool set_crtc_expectation_; - bool add_framebuffer_expectation_; - bool page_flip_expectation_; - - DISALLOW_COPY_AND_ASSIGN(MockDriWrapper); -}; - -class MockDriSkBitmap : public gfx::DriSkBitmap { - public: - MockDriSkBitmap(int fd) : DriSkBitmap(fd) {} - virtual ~MockDriSkBitmap() {} - - virtual bool Initialize() OVERRIDE { - return allocPixels(); - } - private: - DISALLOW_COPY_AND_ASSIGN(MockDriSkBitmap); -}; - -class MockDriSurface : public gfx::DriSurface { - public: - MockDriSurface(gfx::HardwareDisplayController* controller) - : DriSurface(controller) {} - virtual ~MockDriSurface() {} - - private: - virtual gfx::DriSkBitmap* CreateBuffer() OVERRIDE { - return new MockDriSkBitmap(kFd); - } - DISALLOW_COPY_AND_ASSIGN(MockDriSurface); -}; - -} // namespace - -class HardwareDisplayControllerTest : public testing::Test { - public: - HardwareDisplayControllerTest() {} - virtual ~HardwareDisplayControllerTest() {} - - virtual void SetUp() OVERRIDE; - virtual void TearDown() OVERRIDE; - protected: - scoped_ptr<gfx::HardwareDisplayController> controller_; - scoped_ptr<MockDriWrapper> drm_; - - private: - DISALLOW_COPY_AND_ASSIGN(HardwareDisplayControllerTest); -}; - -void HardwareDisplayControllerTest::SetUp() { - controller_.reset(new gfx::HardwareDisplayController()); - drm_.reset(new MockDriWrapper(kFd)); -} - -void HardwareDisplayControllerTest::TearDown() { - controller_.reset(); - drm_.reset(); -} - -TEST_F(HardwareDisplayControllerTest, CheckInitialState) { - EXPECT_EQ(gfx::HardwareDisplayController::UNASSOCIATED, - controller_->get_state()); -} - -TEST_F(HardwareDisplayControllerTest, - CheckStateAfterControllerIsInitialized) { - controller_->SetControllerInfo( - drm_.get(), kConnectorId, kCrtcId, kDPMSPropertyId, kDefaultMode); - - EXPECT_EQ(1, drm_->get_get_crtc_call_count()); - EXPECT_EQ(gfx::HardwareDisplayController::UNINITIALIZED, - controller_->get_state()); -} - -TEST_F(HardwareDisplayControllerTest, CheckStateAfterSurfaceIsBound) { - controller_->SetControllerInfo( - drm_.get(), kConnectorId, kCrtcId, kDPMSPropertyId, kDefaultMode); - scoped_ptr<gfx::DriSurface> surface( - new MockDriSurface(controller_.get())); - - EXPECT_TRUE(surface->Initialize()); - EXPECT_TRUE(controller_->BindSurfaceToController(surface.Pass())); - - EXPECT_EQ(2, drm_->get_add_framebuffer_call_count()); - EXPECT_EQ(gfx::HardwareDisplayController::SURFACE_INITIALIZED, - controller_->get_state()); -} - -TEST_F(HardwareDisplayControllerTest, CheckStateIfBindingFails) { - drm_->set_add_framebuffer_expectation(false); - - controller_->SetControllerInfo( - drm_.get(), kConnectorId, kCrtcId, kDPMSPropertyId, kDefaultMode); - scoped_ptr<gfx::DriSurface> surface( - new MockDriSurface(controller_.get())); - - EXPECT_TRUE(surface->Initialize()); - EXPECT_FALSE(controller_->BindSurfaceToController(surface.Pass())); - - EXPECT_EQ(1, drm_->get_add_framebuffer_call_count()); - EXPECT_EQ(gfx::HardwareDisplayController::FAILED, - controller_->get_state()); -} - -TEST_F(HardwareDisplayControllerTest, CheckStateAfterPageFlip) { - controller_->SetControllerInfo( - drm_.get(), kConnectorId, kCrtcId, kDPMSPropertyId, kDefaultMode); - scoped_ptr<gfx::DriSurface> surface( - new MockDriSurface(controller_.get())); - - EXPECT_TRUE(surface->Initialize()); - EXPECT_TRUE(controller_->BindSurfaceToController(surface.Pass())); - - controller_->SchedulePageFlip(); - - EXPECT_EQ(gfx::HardwareDisplayController::INITIALIZED, - controller_->get_state()); -} - -TEST_F(HardwareDisplayControllerTest, CheckStateIfModesetFails) { - drm_->set_set_crtc_expectation(false); - - controller_->SetControllerInfo( - drm_.get(), kConnectorId, kCrtcId, kDPMSPropertyId, kDefaultMode); - scoped_ptr<gfx::DriSurface> surface( - new MockDriSurface(controller_.get())); - - EXPECT_TRUE(surface->Initialize()); - EXPECT_TRUE(controller_->BindSurfaceToController(surface.Pass())); - - controller_->SchedulePageFlip(); - - EXPECT_EQ(gfx::HardwareDisplayController::FAILED, - controller_->get_state()); -} - -TEST_F(HardwareDisplayControllerTest, CheckStateIfPageFlipFails) { - drm_->set_page_flip_expectation(false); - - controller_->SetControllerInfo( - drm_.get(), kConnectorId, kCrtcId, kDPMSPropertyId, kDefaultMode); - scoped_ptr<gfx::DriSurface> surface( - new MockDriSurface(controller_.get())); - - EXPECT_TRUE(surface->Initialize()); - EXPECT_TRUE(controller_->BindSurfaceToController(surface.Pass())); - - controller_->SchedulePageFlip(); - - EXPECT_EQ(gfx::HardwareDisplayController::FAILED, - controller_->get_state()); -} - -TEST_F(HardwareDisplayControllerTest, CheckProperDestruction) { - controller_->SetControllerInfo( - drm_.get(), kConnectorId, kCrtcId, kDPMSPropertyId, kDefaultMode); - scoped_ptr<gfx::DriSurface> surface( - new MockDriSurface(controller_.get())); - - EXPECT_TRUE(surface->Initialize()); - EXPECT_TRUE(controller_->BindSurfaceToController(surface.Pass())); - - EXPECT_EQ(gfx::HardwareDisplayController::SURFACE_INITIALIZED, - controller_->get_state()); - - controller_.reset(); - - EXPECT_EQ(2, drm_->get_remove_framebuffer_call_count()); - EXPECT_EQ(1, drm_->get_restore_crtc_call_count()); - EXPECT_EQ(1, drm_->get_free_crtc_call_count()); -} diff --git a/chromium/ui/gfx/ozone/impl/file_surface_factory.cc b/chromium/ui/gfx/ozone/impl/file_surface_factory.cc deleted file mode 100644 index 9d34f7f95a3..00000000000 --- a/chromium/ui/gfx/ozone/impl/file_surface_factory.cc +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/gfx/ozone/impl/file_surface_factory.h" - -#include "base/bind.h" -#include "base/file_util.h" -#include "base/location.h" -#include "base/stl_util.h" -#include "base/threading/worker_pool.h" -#include "third_party/skia/include/core/SkBitmapDevice.h" -#include "third_party/skia/include/core/SkDevice.h" -#include "ui/gfx/codec/png_codec.h" - -namespace { - -void WriteDataToFile(const base::FilePath& location, - const SkBitmap& bitmap) { - std::vector<unsigned char> png_data; - gfx::PNGCodec::FastEncodeBGRASkBitmap(bitmap, true, &png_data); - file_util::WriteFile(location, - (char*)vector_as_array(&png_data), - png_data.size()); -} - -} - -namespace gfx { - -FileSurfaceFactory::FileSurfaceFactory( - const base::FilePath& dump_location) - : location_(dump_location) { - CHECK(!base::DirectoryExists(location_)) - << "Location cannot be a directory (" << location_.value() << ")"; - CHECK(!base::PathExists(location_) || base::PathIsWritable(location_)); -} - -FileSurfaceFactory::~FileSurfaceFactory() {} - -SurfaceFactoryOzone::HardwareState -FileSurfaceFactory::InitializeHardware() { - return INITIALIZED; -} - -void FileSurfaceFactory::ShutdownHardware() { -} - -AcceleratedWidget FileSurfaceFactory::GetAcceleratedWidget() { - return 1; -} - -AcceleratedWidget FileSurfaceFactory::RealizeAcceleratedWidget( - AcceleratedWidget widget) { - return 1; -} - -bool FileSurfaceFactory::LoadEGLGLES2Bindings( - AddGLLibraryCallback add_gl_library, - SetGLGetProcAddressProcCallback set_gl_get_proc_address) { - return false; -} - -bool FileSurfaceFactory::AttemptToResizeAcceleratedWidget( - AcceleratedWidget widget, - const Rect& bounds) { - device_ = skia::AdoptRef(new SkBitmapDevice(SkBitmap::kARGB_8888_Config, - bounds.width(), - bounds.height())); - canvas_ = skia::AdoptRef(new SkCanvas(device_.get())); - return true; -} - -bool FileSurfaceFactory::SchedulePageFlip(AcceleratedWidget widget) { - SkBitmap bitmap; - bitmap.setConfig(SkBitmap::kARGB_8888_Config, - device_->width(), - device_->height()); - - if (canvas_->readPixels(&bitmap, 0, 0)) { - base::WorkerPool::PostTask(FROM_HERE, - base::Bind(&WriteDataToFile, location_, bitmap), - true); - } - return true; -} - -SkCanvas* FileSurfaceFactory::GetCanvasForWidget(AcceleratedWidget w) { - return canvas_.get(); -} - -VSyncProvider* FileSurfaceFactory::GetVSyncProvider(AcceleratedWidget w) { - return NULL; -} - -} // namespace gfx diff --git a/chromium/ui/gfx/ozone/impl/file_surface_factory.h b/chromium/ui/gfx/ozone/impl/file_surface_factory.h deleted file mode 100644 index 19f1c946a33..00000000000 --- a/chromium/ui/gfx/ozone/impl/file_surface_factory.h +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_GFX_OZONE_IMPL_FILE_SURFACE_FACTORY_H_ -#define UI_GFX_OZONE_IMPL_FILE_SURFACE_FACTORY_H_ - -#include "base/files/file_path.h" -#include "base/memory/scoped_ptr.h" -#include "ui/gfx/native_widget_types.h" -#include "ui/gfx/ozone/surface_factory_ozone.h" -#include "ui/gfx/skia_util.h" - -class SkBitmapDevice; -class SkCanvas; - -namespace gfx { - -class GFX_EXPORT FileSurfaceFactory : public SurfaceFactoryOzone { - public: - explicit FileSurfaceFactory(const base::FilePath& dump_location); - virtual ~FileSurfaceFactory(); - - private: - // SurfaceFactoryOzone: - virtual HardwareState InitializeHardware() OVERRIDE; - virtual void ShutdownHardware() OVERRIDE; - virtual AcceleratedWidget GetAcceleratedWidget() OVERRIDE; - virtual AcceleratedWidget RealizeAcceleratedWidget( - AcceleratedWidget widget) OVERRIDE; - virtual bool LoadEGLGLES2Bindings( - AddGLLibraryCallback add_gl_library, - SetGLGetProcAddressProcCallback set_gl_get_proc_address) OVERRIDE; - virtual bool AttemptToResizeAcceleratedWidget(AcceleratedWidget widget, - const Rect& bounds) OVERRIDE; - virtual bool SchedulePageFlip(AcceleratedWidget widget) OVERRIDE; - virtual SkCanvas* GetCanvasForWidget(AcceleratedWidget widget) OVERRIDE; - virtual VSyncProvider* GetVSyncProvider(AcceleratedWidget widget) OVERRIDE; - - base::FilePath location_; - skia::RefPtr<SkBitmapDevice> device_; - skia::RefPtr<SkCanvas> canvas_; - - DISALLOW_COPY_AND_ASSIGN(FileSurfaceFactory); -}; - -} // namespace gfx - -#endif // UI_GFX_OZONE_IMPL_FILE_SURFACE_FACTORY_H_ diff --git a/chromium/ui/gfx/ozone/surface_factory_ozone.cc b/chromium/ui/gfx/ozone/surface_factory_ozone.cc deleted file mode 100644 index ea8e2f1a0b2..00000000000 --- a/chromium/ui/gfx/ozone/surface_factory_ozone.cc +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/gfx/ozone/surface_factory_ozone.h" - -#include <stdlib.h> - -#include "base/command_line.h" - -namespace gfx { - -// static -SurfaceFactoryOzone* SurfaceFactoryOzone::impl_ = NULL; - -class SurfaceFactoryOzoneStub : public SurfaceFactoryOzone { - public: - SurfaceFactoryOzoneStub() {} - virtual ~SurfaceFactoryOzoneStub() {} - - virtual HardwareState InitializeHardware() OVERRIDE { return INITIALIZED; } - virtual void ShutdownHardware() OVERRIDE {} - virtual gfx::AcceleratedWidget GetAcceleratedWidget() OVERRIDE { return 0; } - virtual gfx::AcceleratedWidget RealizeAcceleratedWidget( - gfx::AcceleratedWidget w) OVERRIDE { - return 0; - } - virtual bool LoadEGLGLES2Bindings( - AddGLLibraryCallback add_gl_library, - SetGLGetProcAddressProcCallback set_gl_get_proc_address) OVERRIDE { - return true; - } - virtual bool AttemptToResizeAcceleratedWidget( - gfx::AcceleratedWidget w, - const gfx::Rect& bounds) OVERRIDE { - return false; - } - virtual gfx::VSyncProvider* GetVSyncProvider( - gfx::AcceleratedWidget w) OVERRIDE { - return NULL; - } -}; - -SurfaceFactoryOzone::SurfaceFactoryOzone() { -} - -SurfaceFactoryOzone::~SurfaceFactoryOzone() { -} - -SurfaceFactoryOzone* SurfaceFactoryOzone::GetInstance() { - CHECK(impl_) << "No SurfaceFactoryOzone implementation set."; - return impl_; -} - -void SurfaceFactoryOzone::SetInstance(SurfaceFactoryOzone* impl) { - impl_ = impl; -} - -const char* SurfaceFactoryOzone::DefaultDisplaySpec() { - char* envvar = getenv("ASH_DISPLAY_SPEC"); - if (envvar) - return envvar; - return "720x1280*2"; -} - -gfx::Screen* SurfaceFactoryOzone::CreateDesktopScreen() { - return NULL; -} - -intptr_t SurfaceFactoryOzone::GetNativeDisplay() { - return 0; -} - -bool SurfaceFactoryOzone::SchedulePageFlip(gfx::AcceleratedWidget w) { - return true; -} - -SkCanvas* SurfaceFactoryOzone::GetCanvasForWidget(gfx::AcceleratedWidget w) { - return NULL; -} - -const int32* SurfaceFactoryOzone::GetEGLSurfaceProperties( - const int32* desired_attributes) { - return desired_attributes; -} - -// static -SurfaceFactoryOzone* SurfaceFactoryOzone::CreateTestHelper() { - return new SurfaceFactoryOzoneStub; -} - -} // namespace gfx diff --git a/chromium/ui/gfx/ozone/surface_factory_ozone.h b/chromium/ui/gfx/ozone/surface_factory_ozone.h deleted file mode 100644 index c09a65ff8c7..00000000000 --- a/chromium/ui/gfx/ozone/surface_factory_ozone.h +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_GFX_OZONE_SURFACE_LNUX_FACTORY_OZONE_H_ -#define UI_GFX_OZONE_SURFACE_LNUX_FACTORY_OZONE_H_ - -#include "base/callback.h" -#include "base/native_library.h" -#include "ui/gfx/gfx_export.h" -#include "ui/gfx/native_widget_types.h" -#include "ui/gfx/rect.h" - -class SkCanvas; - -namespace gfx { -class Screen; -class VSyncProvider; - -// The Ozone interface allows external implementations to hook into Chromium to -// provide a system specific implementation. The Ozone interface supports two -// drawing modes: 1) accelerated drawing through EGL and 2) software drawing -// through Skia. -// -// The following functionality is specific to the drawing mode and may not have -// any meaningful implementation in the other mode. An implementation must -// provide functionality for at least one mode. -// -// 1) Accelerated Drawing (EGL path): -// -// The following functions are specific to EGL: -// - GetNativeDisplay -// - LoadEGLGLES2Bindings -// - GetEGLSurfaceProperties (optional if the properties match the default -// Chromium ones). -// -// 2) Software Drawing (Skia): -// -// The following function is specific to the software path: -// - GetCanvasForWidget -// -// The accelerated path can optionally provide support for the software drawing -// path. -// -// The remaining functions are not covered since they are needed in both drawing -// modes (See comments bellow for descriptions). -class GFX_EXPORT SurfaceFactoryOzone { - public: - // Describes the state of the hardware after initialization. - enum HardwareState { - UNINITIALIZED, - INITIALIZED, - FAILED, - }; - - typedef void*(*GLGetProcAddressProc)(const char* name); - typedef base::Callback<void(base::NativeLibrary)> AddGLLibraryCallback; - typedef base::Callback<void(GLGetProcAddressProc)> - SetGLGetProcAddressProcCallback; - - SurfaceFactoryOzone(); - virtual ~SurfaceFactoryOzone(); - - // Returns the instance - static SurfaceFactoryOzone* GetInstance(); - - // Returns a display spec as in |CreateDisplayFromSpec| for the default - // native surface. - virtual const char* DefaultDisplaySpec(); - - // Sets the implementation delegate. Ownership is retained by the caller. - static void SetInstance(SurfaceFactoryOzone* impl); - - // TODO(rjkroege): decide how to separate screen/display stuff from SFOz - // This method implements gfx::Screen, particularly useful in Desktop Aura. - virtual gfx::Screen* CreateDesktopScreen(); - - // Configures the display hardware. Must be called from within the GPU - // process before the sandbox has been activated. - virtual HardwareState InitializeHardware() = 0; - - // Cleans up display hardware state. Call this from within the GPU process. - // This method must be safe to run inside of the sandbox. - virtual void ShutdownHardware() = 0; - - // Returns native platform display handle. This is used to obtain the EGL - // display connection for the native display. - virtual intptr_t GetNativeDisplay(); - - // Obtains an AcceleratedWidget backed by a native Linux framebuffer. - // The returned AcceleratedWidget is an opaque token that must realized - // before it can be used to create a GL surface. - virtual gfx::AcceleratedWidget GetAcceleratedWidget() = 0; - - // Realizes an AcceleratedWidget so that the returned AcceleratedWidget - // can be used to to create a GLSurface. This method may only be called in - // a process that has a valid GL context. - virtual gfx::AcceleratedWidget RealizeAcceleratedWidget( - gfx::AcceleratedWidget w) = 0; - - // Sets up GL bindings for the native surface. Takes two callback parameters - // that allow Ozone to register the GL bindings. - virtual bool LoadEGLGLES2Bindings( - AddGLLibraryCallback add_gl_library, - SetGLGetProcAddressProcCallback set_gl_get_proc_address) = 0; - - // If possible attempts to resize the given AcceleratedWidget instance and if - // a resize action was performed returns true, otherwise false (native - // hardware may only support a single fixed size). - virtual bool AttemptToResizeAcceleratedWidget( - gfx::AcceleratedWidget w, - const gfx::Rect& bounds) = 0; - - // Called after the appropriate GL swap buffers command. Used if extra work - // is needed to perform the actual buffer swap. - virtual bool SchedulePageFlip(gfx::AcceleratedWidget w); - - // Returns a SkCanvas for the backing buffers. Drawing to the canvas will draw - // to the native surface. The canvas is intended for use when no EGL - // acceleration is possible. Its implementation is optional when an EGL - // backend is provided for rendering. - virtual SkCanvas* GetCanvasForWidget(gfx::AcceleratedWidget w); - - // Returns a gfx::VsyncProvider for the provided AcceleratedWidget. Note - // that this may be called after we have entered the sandbox so if there are - // operations (e.g. opening a file descriptor providing vsync events) that - // must be done outside of the sandbox, they must have been completed - // in InitializeHardware. Returns NULL on error. - virtual gfx::VSyncProvider* GetVSyncProvider(gfx::AcceleratedWidget w) = 0; - - // Returns an array of EGL properties, which can be used in any EGL function - // used to select a display configuration. Note that all properties should be - // immediately followed by the corresponding desired value and array should be - // terminated with EGL_NONE. Ownership of the array is not transferred to - // caller. desired_list contains list of desired EGL properties and values. - virtual const int32* GetEGLSurfaceProperties(const int32* desired_list); - - // Create a default SufaceFactoryOzone implementation useful for tests. - static SurfaceFactoryOzone* CreateTestHelper(); - - private: - static SurfaceFactoryOzone* impl_; // not owned -}; - -} // namespace gfx - -#endif // UI_GFX_OZONE_SURFACE_LNUX_FACTORY_OZONE_H_ diff --git a/chromium/ui/gfx/pango_util.cc b/chromium/ui/gfx/pango_util.cc index 1f8ac261b59..56503c6a01f 100644 --- a/chromium/ui/gfx/pango_util.cc +++ b/chromium/ui/gfx/pango_util.cc @@ -23,10 +23,6 @@ #include "ui/gfx/rect.h" #include "ui/gfx/text_utils.h" -#if defined(TOOLKIT_GTK) -#include <gdk/gdk.h> -#endif - namespace gfx { namespace { @@ -116,12 +112,8 @@ float GetPixelsInPoint() { } // namespace PangoContext* GetPangoContext() { -#if defined(TOOLKIT_GTK) - return gdk_pango_context_get(); -#else PangoFontMap* font_map = pango_cairo_font_map_get_default(); return pango_font_map_create_context(font_map); -#endif } double GetPangoResolution() { @@ -211,7 +203,7 @@ static void SetupPangoLayoutWithoutFont( // Set text and accelerator character if needed. if (flags & Canvas::SHOW_PREFIX) { // Escape the text string to be used as markup. - std::string utf8 = UTF16ToUTF8(text); + std::string utf8 = base::UTF16ToUTF8(text); gchar* escaped_text = g_markup_escape_text(utf8.c_str(), utf8.size()); pango_layout_set_markup_with_accel(layout, escaped_text, @@ -229,9 +221,9 @@ static void SetupPangoLayoutWithoutFont( RemoveAcceleratorChar(text, static_cast<base::char16>(kAcceleratorChar), NULL, NULL); - utf8 = UTF16ToUTF8(accelerator_removed); + utf8 = base::UTF16ToUTF8(accelerator_removed); } else { - utf8 = UTF16ToUTF8(text); + utf8 = base::UTF16ToUTF8(text); } pango_layout_set_text(layout, utf8.data(), utf8.size()); diff --git a/chromium/ui/gfx/path.h b/chromium/ui/gfx/path.h index 257a65c0102..b0f07279c7f 100644 --- a/chromium/ui/gfx/path.h +++ b/chromium/ui/gfx/path.h @@ -32,7 +32,7 @@ class GFX_EXPORT Path : public SkPath { ~Path(); -#if defined(USE_AURA) || defined(OS_WIN) || defined(USE_X11) +#if defined(USE_AURA) || defined(USE_X11) // Creates a NativeRegion from the path. The caller is responsible for freeing // resources used by this region. This only supports polygon paths. NativeRegion CreateNativeRegion() const; diff --git a/chromium/ui/gfx/path_gtk.cc b/chromium/ui/gfx/path_gtk.cc deleted file mode 100644 index 99dad2be43b..00000000000 --- a/chromium/ui/gfx/path_gtk.cc +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/gfx/path.h" - -#include <gdk/gdk.h> - -#include "base/command_line.h" -#include "base/memory/scoped_ptr.h" - -namespace gfx { - -GdkRegion* Path::CreateNativeRegion() const { - int point_count = getPoints(NULL, 0); - if (point_count <= 1) { - // NOTE: ideally this would return gdk_empty_region, but that returns a - // region with nothing in it. - return NULL; - } - - scoped_ptr<SkPoint[]> points(new SkPoint[point_count]); - getPoints(points.get(), point_count); - - scoped_ptr<GdkPoint[]> gdk_points(new GdkPoint[point_count]); - for (int i = 0; i < point_count; ++i) { - gdk_points[i].x = SkScalarRound(points[i].fX); - gdk_points[i].y = SkScalarRound(points[i].fY); - } - - return gdk_region_polygon(gdk_points.get(), point_count, GDK_EVEN_ODD_RULE); -} - -// static -NativeRegion Path::IntersectRegions(NativeRegion r1, NativeRegion r2) { - GdkRegion* copy = gdk_region_copy(r1); - gdk_region_intersect(copy, r2); - return copy; -} - -// static -NativeRegion Path::CombineRegions(NativeRegion r1, NativeRegion r2) { - GdkRegion* copy = gdk_region_copy(r1); - gdk_region_union(copy, r2); - return copy; -} - -// static -NativeRegion Path::SubtractRegion(NativeRegion r1, NativeRegion r2) { - GdkRegion* copy = gdk_region_copy(r1); - gdk_region_subtract(copy, r2); - return copy; -} - -} // namespace gfx diff --git a/chromium/ui/gfx/path_win.cc b/chromium/ui/gfx/path_win.cc index bce62ac849b..b28ea7a2c7b 100644 --- a/chromium/ui/gfx/path_win.cc +++ b/chromium/ui/gfx/path_win.cc @@ -30,8 +30,8 @@ HRGN CreateHRGNFromSkPath(const SkPath& path) { path.getPoints(points.get(), point_count); scoped_ptr<POINT[]> windows_points(new POINT[point_count]); for (int i = 0; i < point_count; ++i) { - windows_points[i].x = SkScalarRound(points[i].fX); - windows_points[i].y = SkScalarRound(points[i].fY); + windows_points[i].x = SkScalarRoundToInt(points[i].fX); + windows_points[i].y = SkScalarRoundToInt(points[i].fY); } return ::CreatePolygonRgn(windows_points.get(), point_count, ALTERNATE); diff --git a/chromium/ui/gfx/path_x11.cc b/chromium/ui/gfx/path_x11.cc index 2fe764fbd4c..caea2ec12e6 100644 --- a/chromium/ui/gfx/path_x11.cc +++ b/chromium/ui/gfx/path_x11.cc @@ -4,6 +4,8 @@ #include "ui/gfx/path_x11.h" +#include <X11/Xlib.h> +#include <X11/Xregion.h> #include <X11/Xutil.h> #include "base/memory/scoped_ptr.h" @@ -33,8 +35,8 @@ Region CreateRegionFromSkPath(const SkPath& path) { path.getPoints(points.get(), point_count); scoped_ptr<XPoint[]> x11_points(new XPoint[point_count]); for (int i = 0; i < point_count; ++i) { - x11_points[i].x = SkScalarRound(points[i].fX); - x11_points[i].y = SkScalarRound(points[i].fY); + x11_points[i].x = SkScalarRoundToInt(points[i].fX); + x11_points[i].y = SkScalarRoundToInt(points[i].fY); } return XPolygonRegion(x11_points.get(), point_count, EvenOddRule); diff --git a/chromium/ui/gfx/path_x11.h b/chromium/ui/gfx/path_x11.h index c6e750742ec..6e3a1568293 100644 --- a/chromium/ui/gfx/path_x11.h +++ b/chromium/ui/gfx/path_x11.h @@ -5,14 +5,13 @@ #ifndef UI_GFX_PATH_X11_H_ #define UI_GFX_PATH_X11_H_ -#include <X11/Xlib.h> -#include <X11/Xregion.h> - #include "ui/gfx/gfx_export.h" class SkPath; class SkRegion; +typedef struct _XRegion REGION; + namespace gfx { // Creates a new REGION given |region|. The caller is responsible for destroying diff --git a/chromium/ui/gfx/platform_font.h b/chromium/ui/gfx/platform_font.h index bf470983e12..27f39b751a9 100644 --- a/chromium/ui/gfx/platform_font.h +++ b/chromium/ui/gfx/platform_font.h @@ -45,13 +45,6 @@ class GFX_EXPORT PlatformFont : public base::RefCounted<PlatformFont> { // Returns the cap height of the font. virtual int GetCapHeight() const = 0; - // Returns the average character width for the font. - virtual int GetAverageCharacterWidth() const = 0; - - // Returns the number of horizontal pixels needed to display the specified - // string. - virtual int GetStringWidth(const base::string16& text) const = 0; - // Returns the expected number of horizontal pixels needed to display the // specified length of characters. Call GetStringWidth() to retrieve the // actual number. diff --git a/chromium/ui/gfx/platform_font_ios.h b/chromium/ui/gfx/platform_font_ios.h index ed93a6b6e66..3323f820c45 100644 --- a/chromium/ui/gfx/platform_font_ios.h +++ b/chromium/ui/gfx/platform_font_ios.h @@ -21,8 +21,6 @@ class PlatformFontIOS : public PlatformFont { virtual int GetHeight() const OVERRIDE; virtual int GetBaseline() const OVERRIDE; virtual int GetCapHeight() const OVERRIDE; - virtual int GetAverageCharacterWidth() const OVERRIDE; - virtual int GetStringWidth(const base::string16& text) const OVERRIDE; virtual int GetExpectedTextWidth(int length) const OVERRIDE; virtual int GetStyle() const OVERRIDE; virtual std::string GetFontName() const OVERRIDE; diff --git a/chromium/ui/gfx/platform_font_ios.mm b/chromium/ui/gfx/platform_font_ios.mm index 32dcca4e395..aba239b732d 100644 --- a/chromium/ui/gfx/platform_font_ios.mm +++ b/chromium/ui/gfx/platform_font_ios.mm @@ -55,15 +55,6 @@ int PlatformFontIOS::GetCapHeight() const { return cap_height_; } -int PlatformFontIOS::GetAverageCharacterWidth() const { - return average_width_; -} - -int PlatformFontIOS::GetStringWidth(const base::string16& text) const { - NSString* ns_text = base::SysUTF16ToNSString(text); - return [ns_text sizeWithFont:GetNativeFont()].width; -} - int PlatformFontIOS::GetExpectedTextWidth(int length) const { return length * average_width_; } diff --git a/chromium/ui/gfx/platform_font_mac.h b/chromium/ui/gfx/platform_font_mac.h index 2f5f8deb3fe..8c469882ff1 100644 --- a/chromium/ui/gfx/platform_font_mac.h +++ b/chromium/ui/gfx/platform_font_mac.h @@ -23,8 +23,6 @@ class PlatformFontMac : public PlatformFont { virtual int GetHeight() const OVERRIDE; virtual int GetBaseline() const OVERRIDE; virtual int GetCapHeight() const OVERRIDE; - virtual int GetAverageCharacterWidth() const OVERRIDE; - virtual int GetStringWidth(const base::string16& text) const OVERRIDE; virtual int GetExpectedTextWidth(int length) const OVERRIDE; virtual int GetStyle() const OVERRIDE; virtual std::string GetFontName() const OVERRIDE; diff --git a/chromium/ui/gfx/platform_font_mac.mm b/chromium/ui/gfx/platform_font_mac.mm index 877507eb47b..53e3013170c 100644 --- a/chromium/ui/gfx/platform_font_mac.mm +++ b/chromium/ui/gfx/platform_font_mac.mm @@ -105,15 +105,6 @@ int PlatformFontMac::GetCapHeight() const { return cap_height_; } -int PlatformFontMac::GetAverageCharacterWidth() const { - return average_width_; -} - -int PlatformFontMac::GetStringWidth(const base::string16& text) const { - return Canvas::GetStringWidth(text, - Font(const_cast<PlatformFontMac*>(this))); -} - int PlatformFontMac::GetExpectedTextWidth(int length) const { return length * average_width_; } diff --git a/chromium/ui/gfx/platform_font_mac_unittest.mm b/chromium/ui/gfx/platform_font_mac_unittest.mm index 088d4a9bd2d..2584cb50d31 100644 --- a/chromium/ui/gfx/platform_font_mac_unittest.mm +++ b/chromium/ui/gfx/platform_font_mac_unittest.mm @@ -12,20 +12,20 @@ TEST(PlatformFontMacTest, DeriveFont) { gfx::Font base_font("Helvetica", 13); // Bold - gfx::Font bold_font(base_font.DeriveFont(0, gfx::Font::BOLD)); + gfx::Font bold_font(base_font.Derive(0, gfx::Font::BOLD)); NSFontTraitMask traits = [[NSFontManager sharedFontManager] traitsOfFont:bold_font.GetNativeFont()]; EXPECT_EQ(NSBoldFontMask, traits); // Italic - gfx::Font italic_font(base_font.DeriveFont(0, gfx::Font::ITALIC)); + gfx::Font italic_font(base_font.Derive(0, gfx::Font::ITALIC)); traits = [[NSFontManager sharedFontManager] traitsOfFont:italic_font.GetNativeFont()]; EXPECT_EQ(NSItalicFontMask, traits); // Bold italic - gfx::Font bold_italic_font(base_font.DeriveFont(0, gfx::Font::BOLD | - gfx::Font::ITALIC)); + gfx::Font bold_italic_font(base_font.Derive( + 0, gfx::Font::BOLD | gfx::Font::ITALIC)); traits = [[NSFontManager sharedFontManager] traitsOfFont:bold_italic_font.GetNativeFont()]; EXPECT_EQ(static_cast<NSFontTraitMask>(NSBoldFontMask | NSItalicFontMask), diff --git a/chromium/ui/gfx/platform_font_pango.cc b/chromium/ui/gfx/platform_font_pango.cc index db1d7dce351..efc940bd186 100644 --- a/chromium/ui/gfx/platform_font_pango.cc +++ b/chromium/ui/gfx/platform_font_pango.cc @@ -19,12 +19,10 @@ #include "third_party/skia/include/core/SkTypeface.h" #include "ui/gfx/canvas.h" #include "ui/gfx/font.h" +#include "ui/gfx/font_list.h" +#include "ui/gfx/linux_font_delegate.h" #include "ui/gfx/pango_util.h" - -#if defined(TOOLKIT_GTK) -#include <gdk/gdk.h> -#include <gtk/gtk.h> -#endif +#include "ui/gfx/text_utils.h" namespace { @@ -184,23 +182,7 @@ int PlatformFontPango::GetBaseline() const { } int PlatformFontPango::GetCapHeight() const { - // Return the ascent as an approximation because Pango doesn't support cap - // height. - // TODO(yukishiino): Come up with a better approximation of cap height, or - // support cap height metrics. Another option is to have a hard-coded table - // of cap height for major fonts used in Chromium/Chrome. - // See http://crbug.com/249507 - return ascent_pixels_; -} - -int PlatformFontPango::GetAverageCharacterWidth() const { - const_cast<PlatformFontPango*>(this)->InitPangoMetrics(); - return SkScalarRound(average_width_pixels_); -} - -int PlatformFontPango::GetStringWidth(const base::string16& text) const { - return Canvas::GetStringWidth(text, - Font(const_cast<PlatformFontPango*>(this))); + return cap_height_pixels_; } int PlatformFontPango::GetExpectedTextWidth(int length) const { @@ -269,31 +251,19 @@ PlatformFontPango::~PlatformFontPango() {} // static std::string PlatformFontPango::GetDefaultFont() { -#if !defined(TOOLKIT_GTK) #if defined(OS_CHROMEOS) // Font name must have been provided by way of SetDefaultFontDescription(). CHECK(default_font_description_); return *default_font_description_; #else + const gfx::LinuxFontDelegate* delegate = gfx::LinuxFontDelegate::instance(); + if (delegate) + return delegate->GetDefaultFontName(); + return "sans 10"; #endif // defined(OS_CHROMEOS) -#else - GtkSettings* settings = gtk_settings_get_default(); - - gchar* font_name = NULL; - g_object_get(settings, "gtk-font-name", &font_name, NULL); - - // Temporary CHECK for helping track down - // http://code.google.com/p/chromium/issues/detail?id=12530 - CHECK(font_name) << " Unable to get gtk-font-name for default font."; - - std::string default_font = std::string(font_name); - g_free(font_name); - return default_font; -#endif // !defined(TOOLKIT_GTK) } - void PlatformFontPango::InitWithNameAndSize(const std::string& font_name, int font_size) { DCHECK_GT(font_size, 0); @@ -338,8 +308,9 @@ void PlatformFontPango::InitWithTypefaceNameSizeAndStyle( PaintSetup(&paint); paint.getFontMetrics(&metrics); - ascent_pixels_ = SkScalarCeil(-metrics.fAscent); - height_pixels_ = ascent_pixels_ + SkScalarCeil(metrics.fDescent); + ascent_pixels_ = SkScalarCeilToInt(-metrics.fAscent); + height_pixels_ = ascent_pixels_ + SkScalarCeilToInt(metrics.fDescent); + cap_height_pixels_ = SkScalarCeilToInt(metrics.fCapHeight); } void PlatformFontPango::InitFromPlatformFont(const PlatformFontPango* other) { @@ -349,6 +320,7 @@ void PlatformFontPango::InitFromPlatformFont(const PlatformFontPango* other) { style_ = other->style_; height_pixels_ = other->height_pixels_; ascent_pixels_ = other->ascent_pixels_; + cap_height_pixels_ = other->cap_height_pixels_; pango_metrics_inited_ = other->pango_metrics_inited_; average_width_pixels_ = other->average_width_pixels_; underline_position_pixels_ = other->underline_position_pixels_; @@ -392,7 +364,9 @@ void PlatformFontPango::InitPangoMetrics() { // Yes, this is how Microsoft recommends calculating the dialog unit // conversions. const int text_width_pixels = GetStringWidth( - ASCIIToUTF16("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")); + base::ASCIIToUTF16( + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"), + FontList(Font(this))); const double dialog_units_pixels = (text_width_pixels / 26 + 1) / 2; average_width_pixels_ = std::min(pango_width_pixels, dialog_units_pixels); } diff --git a/chromium/ui/gfx/platform_font_pango.h b/chromium/ui/gfx/platform_font_pango.h index 739a633aa47..548b3ac56e7 100644 --- a/chromium/ui/gfx/platform_font_pango.h +++ b/chromium/ui/gfx/platform_font_pango.h @@ -50,8 +50,6 @@ class GFX_EXPORT PlatformFontPango : public PlatformFont { virtual int GetHeight() const OVERRIDE; virtual int GetBaseline() const OVERRIDE; virtual int GetCapHeight() const OVERRIDE; - virtual int GetAverageCharacterWidth() const OVERRIDE; - virtual int GetStringWidth(const base::string16& text) const OVERRIDE; virtual int GetExpectedTextWidth(int length) const OVERRIDE; virtual int GetStyle() const OVERRIDE; virtual std::string GetFontName() const OVERRIDE; @@ -104,6 +102,7 @@ class GFX_EXPORT PlatformFontPango : public PlatformFont { // Cached metrics, generated at construction. int height_pixels_; int ascent_pixels_; + int cap_height_pixels_; // The pango metrics are much more expensive so we wait until we need them // to compute them. diff --git a/chromium/ui/gfx/platform_font_win.cc b/chromium/ui/gfx/platform_font_win.cc index 0209be6673e..4acaf64b34a 100644 --- a/chromium/ui/gfx/platform_font_win.cc +++ b/chromium/ui/gfx/platform_font_win.cc @@ -101,7 +101,7 @@ Font PlatformFontWin::DeriveFontWithHeight(int height, int style) { int font_height = font.GetHeight(); int font_size = font.GetFontSize(); while (font_height > height && font_size != min_font_size) { - font = font.DeriveFont(-1, style); + font = font.Derive(-1, style); if (font_height == font.GetHeight() && font_size == font.GetFontSize()) break; font_height = font.GetHeight(); @@ -145,18 +145,9 @@ int PlatformFontWin::GetCapHeight() const { return font_ref_->cap_height(); } -int PlatformFontWin::GetAverageCharacterWidth() const { - return font_ref_->ave_char_width(); -} - -int PlatformFontWin::GetStringWidth(const base::string16& text) const { - return Canvas::GetStringWidth(text, - Font(const_cast<PlatformFontWin*>(this))); -} - int PlatformFontWin::GetExpectedTextWidth(int length) const { return length * std::min(font_ref_->GetDluBaseX(), - GetAverageCharacterWidth()); + font_ref_->ave_char_width()); } int PlatformFontWin::GetStyle() const { @@ -211,7 +202,7 @@ void PlatformFontWin::InitWithCopyOfHFONT(HFONT hfont) { void PlatformFontWin::InitWithFontNameAndSize(const std::string& font_name, int font_size) { HFONT hf = ::CreateFont(-font_size, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - UTF8ToUTF16(font_name).c_str()); + base::UTF8ToUTF16(font_name).c_str()); font_ref_ = CreateHFontRef(hf); } @@ -289,7 +280,7 @@ PlatformFontWin::HFontRef::HFontRef(HFONT hfont, LOGFONT font_info; GetObject(hfont_, sizeof(LOGFONT), &font_info); - font_name_ = UTF16ToUTF8(base::string16(font_info.lfFaceName)); + font_name_ = base::UTF16ToUTF8(base::string16(font_info.lfFaceName)); if (font_info.lfHeight < 0) requested_font_size_ = -font_info.lfHeight; } diff --git a/chromium/ui/gfx/platform_font_win.h b/chromium/ui/gfx/platform_font_win.h index 4826f777ffc..2c6f51e0508 100644 --- a/chromium/ui/gfx/platform_font_win.h +++ b/chromium/ui/gfx/platform_font_win.h @@ -58,8 +58,6 @@ class GFX_EXPORT PlatformFontWin : public PlatformFont { virtual int GetHeight() const OVERRIDE; virtual int GetBaseline() const OVERRIDE; virtual int GetCapHeight() const OVERRIDE; - virtual int GetAverageCharacterWidth() const OVERRIDE; - virtual int GetStringWidth(const base::string16& text) const OVERRIDE; virtual int GetExpectedTextWidth(int length) const OVERRIDE; virtual int GetStyle() const OVERRIDE; virtual std::string GetFontName() const OVERRIDE; diff --git a/chromium/ui/gfx/platform_font_win_unittest.cc b/chromium/ui/gfx/platform_font_win_unittest.cc index bfbe92203d1..1ecdde1d352 100644 --- a/chromium/ui/gfx/platform_font_win_unittest.cc +++ b/chromium/ui/gfx/platform_font_win_unittest.cc @@ -20,15 +20,15 @@ gfx::Font AdjustFontSizeForHeight(const gfx::Font& base_font, Font expected_font = base_font; if (base_font.GetHeight() < target_height) { // Increase size while height is <= |target_height|. - Font larger_font = base_font.DeriveFont(1, 0); + Font larger_font = base_font.Derive(1, 0); while (larger_font.GetHeight() <= target_height) { expected_font = larger_font; - larger_font = larger_font.DeriveFont(1, 0); + larger_font = larger_font.Derive(1, 0); } } else if (expected_font.GetHeight() > target_height) { // Decrease size until height is <= |target_height|. do { - expected_font = expected_font.DeriveFont(-1, 0); + expected_font = expected_font.Derive(-1, 0); } while (expected_font.GetHeight() > target_height); } return expected_font; @@ -60,7 +60,7 @@ TEST(PlatformFontWinTest, DeriveFontWithHeight) { EXPECT_EQ(Font::BOLD, derived_font.GetStyle()); // Test that deriving from the new font has the expected result. - Font rederived_font = derived_font.DeriveFont(1, 0); + Font rederived_font = derived_font.Derive(1, 0); expected_font = Font(derived_font.GetFontName(), derived_font.GetFontSize() + 1); EXPECT_EQ(expected_font.GetFontName(), rederived_font.GetFontName()); diff --git a/chromium/ui/gfx/point.h b/chromium/ui/gfx/point.h index 020c4e1251a..bbca081095d 100644 --- a/chromium/ui/gfx/point.h +++ b/chromium/ui/gfx/point.h @@ -1,90 +1,7 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_GFX_POINT_H_ -#define UI_GFX_POINT_H_ +// TODO(beng): remove once callsites are patched. +#include "ui/gfx/geometry/point.h" -#include "ui/gfx/gfx_export.h" -#include "ui/gfx/point_base.h" -#include "ui/gfx/point_f.h" -#include "ui/gfx/vector2d.h" - -#if defined(OS_WIN) -typedef unsigned long DWORD; -typedef struct tagPOINT POINT; -#elif defined(OS_IOS) -#include <CoreGraphics/CoreGraphics.h> -#elif defined(OS_MACOSX) -#include <ApplicationServices/ApplicationServices.h> -#endif - -namespace gfx { - -// A point has an x and y coordinate. -class GFX_EXPORT Point : public PointBase<Point, int, Vector2d> { - public: - Point() : PointBase<Point, int, Vector2d>(0, 0) {} - Point(int x, int y) : PointBase<Point, int, Vector2d>(x, y) {} -#if defined(OS_WIN) - // |point| is a DWORD value that contains a coordinate. The x-coordinate is - // the low-order short and the y-coordinate is the high-order short. This - // value is commonly acquired from GetMessagePos/GetCursorPos. - explicit Point(DWORD point); - explicit Point(const POINT& point); - Point& operator=(const POINT& point); -#elif defined(OS_MACOSX) - explicit Point(const CGPoint& point); -#endif - - ~Point() {} - -#if defined(OS_WIN) - POINT ToPOINT() const; -#elif defined(OS_MACOSX) - CGPoint ToCGPoint() const; -#endif - - operator PointF() const { - return PointF(x(), y()); - } - - // Returns a string representation of point. - std::string ToString() const; -}; - -inline bool operator==(const Point& lhs, const Point& rhs) { - return lhs.x() == rhs.x() && lhs.y() == rhs.y(); -} - -inline bool operator!=(const Point& lhs, const Point& rhs) { - return !(lhs == rhs); -} - -inline Point operator+(const Point& lhs, const Vector2d& rhs) { - Point result(lhs); - result += rhs; - return result; -} - -inline Point operator-(const Point& lhs, const Vector2d& rhs) { - Point result(lhs); - result -= rhs; - return result; -} - -inline Vector2d operator-(const Point& lhs, const Point& rhs) { - return Vector2d(lhs.x() - rhs.x(), lhs.y() - rhs.y()); -} - -inline Point PointAtOffsetFromOrigin(const Vector2d& offset_from_origin) { - return Point(offset_from_origin.x(), offset_from_origin.y()); -} - -#if !defined(COMPILER_MSVC) -extern template class PointBase<Point, int, Vector2d>; -#endif - -} // namespace gfx - -#endif // UI_GFX_POINT_H_ diff --git a/chromium/ui/gfx/point3_f.h b/chromium/ui/gfx/point3_f.h index 485f6558136..8c57c578f48 100644 --- a/chromium/ui/gfx/point3_f.h +++ b/chromium/ui/gfx/point3_f.h @@ -1,120 +1,7 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_GFX_POINT3_F_H_ -#define UI_GFX_POINT3_F_H_ +// TODO(beng): remove once callsites are patched. +#include "ui/gfx/geometry/point3_f.h" -#include <string> - -#include "ui/gfx/gfx_export.h" -#include "ui/gfx/point_f.h" -#include "ui/gfx/vector3d_f.h" - -namespace gfx { - -// A point has an x, y and z coordinate. -class GFX_EXPORT Point3F { - public: - Point3F() : x_(0), y_(0), z_(0) {} - - Point3F(float x, float y, float z) : x_(x), y_(y), z_(z) {} - - explicit Point3F(const PointF& point) : x_(point.x()), y_(point.y()), z_(0) {} - - ~Point3F() {} - - void Scale(float scale) { - Scale(scale, scale, scale); - } - - void Scale(float x_scale, float y_scale, float z_scale) { - SetPoint(x() * x_scale, y() * y_scale, z() * z_scale); - } - - float x() const { return x_; } - float y() const { return y_; } - float z() const { return z_; } - - void set_x(float x) { x_ = x; } - void set_y(float y) { y_ = y; } - void set_z(float z) { z_ = z; } - - void SetPoint(float x, float y, float z) { - x_ = x; - y_ = y; - z_ = z; - } - - // Offset the point by the given vector. - void operator+=(const Vector3dF& v) { - x_ += v.x(); - y_ += v.y(); - z_ += v.z(); - } - - // Offset the point by the given vector's inverse. - void operator-=(const Vector3dF& v) { - x_ -= v.x(); - y_ -= v.y(); - z_ -= v.z(); - } - - // Returns the squared euclidean distance between two points. - float SquaredDistanceTo(const Point3F& other) const { - float dx = x_ - other.x_; - float dy = y_ - other.y_; - float dz = z_ - other.z_; - return dx * dx + dy * dy + dz * dz; - } - - PointF AsPointF() const { return PointF(x_, y_); } - - // Returns a string representation of 3d point. - std::string ToString() const; - - private: - float x_; - float y_; - float z_; - - // copy/assign are allowed. -}; - -inline bool operator==(const Point3F& lhs, const Point3F& rhs) { - return lhs.x() == rhs.x() && lhs.y() == rhs.y() && lhs.z() == rhs.z(); -} - -inline bool operator!=(const Point3F& lhs, const Point3F& rhs) { - return !(lhs == rhs); -} - -// Add a vector to a point, producing a new point offset by the vector. -GFX_EXPORT Point3F operator+(const Point3F& lhs, const Vector3dF& rhs); - -// Subtract a vector from a point, producing a new point offset by the vector's -// inverse. -GFX_EXPORT Point3F operator-(const Point3F& lhs, const Vector3dF& rhs); - -// Subtract one point from another, producing a vector that represents the -// distances between the two points along each axis. -GFX_EXPORT Vector3dF operator-(const Point3F& lhs, const Point3F& rhs); - -inline Point3F PointAtOffsetFromOrigin(const Vector3dF& offset) { - return Point3F(offset.x(), offset.y(), offset.z()); -} - -inline Point3F ScalePoint(const Point3F& p, - float x_scale, - float y_scale, - float z_scale) { - return Point3F(p.x() * x_scale, p.y() * y_scale, p.z() * z_scale); -} - -inline Point3F ScalePoint(const Point3F& p, float scale) { - return ScalePoint(p, scale, scale, scale); -} - -} // namespace gfx - -#endif // UI_GFX_POINT3_F_H_ diff --git a/chromium/ui/gfx/point_conversions.h b/chromium/ui/gfx/point_conversions.h index 942edd711cc..b7c961fe923 100644 --- a/chromium/ui/gfx/point_conversions.h +++ b/chromium/ui/gfx/point_conversions.h @@ -1,24 +1,7 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_GFX_POINT_CONVERSIONS_H_ -#define UI_GFX_POINT_CONVERSIONS_H_ +// TODO(beng): remove once callsites are patched. +#include "ui/gfx/geometry/point_conversions.h" -#include "ui/gfx/point.h" -#include "ui/gfx/point_f.h" - -namespace gfx { - -// Returns a Point with each component from the input PointF floored. -GFX_EXPORT Point ToFlooredPoint(const PointF& point); - -// Returns a Point with each component from the input PointF ceiled. -GFX_EXPORT Point ToCeiledPoint(const PointF& point); - -// Returns a Point with each component from the input PointF rounded. -GFX_EXPORT Point ToRoundedPoint(const PointF& point); - -} // namespace gfx - -#endif // UI_GFX_POINT_CONVERSIONS_H_ diff --git a/chromium/ui/gfx/point_f.h b/chromium/ui/gfx/point_f.h index 664c18d8aa0..f4be8ff0578 100644 --- a/chromium/ui/gfx/point_f.h +++ b/chromium/ui/gfx/point_f.h @@ -1,75 +1,7 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_GFX_POINT_F_H_ -#define UI_GFX_POINT_F_H_ +// TODO(beng): remove once callsites are patched. +#include "ui/gfx/geometry/point_f.h" -#include <string> - -#include "ui/gfx/gfx_export.h" -#include "ui/gfx/point_base.h" -#include "ui/gfx/vector2d_f.h" - -namespace gfx { - -// A floating version of gfx::Point. -class GFX_EXPORT PointF : public PointBase<PointF, float, Vector2dF> { - public: - PointF() : PointBase<PointF, float, Vector2dF>(0, 0) {} - PointF(float x, float y) : PointBase<PointF, float, Vector2dF>(x, y) {} - ~PointF() {} - - void Scale(float scale) { - Scale(scale, scale); - } - - void Scale(float x_scale, float y_scale) { - SetPoint(x() * x_scale, y() * y_scale); - } - - // Returns a string representation of point. - std::string ToString() const; -}; - -inline bool operator==(const PointF& lhs, const PointF& rhs) { - return lhs.x() == rhs.x() && lhs.y() == rhs.y(); -} - -inline bool operator!=(const PointF& lhs, const PointF& rhs) { - return !(lhs == rhs); -} - -inline PointF operator+(const PointF& lhs, const Vector2dF& rhs) { - PointF result(lhs); - result += rhs; - return result; -} - -inline PointF operator-(const PointF& lhs, const Vector2dF& rhs) { - PointF result(lhs); - result -= rhs; - return result; -} - -inline Vector2dF operator-(const PointF& lhs, const PointF& rhs) { - return Vector2dF(lhs.x() - rhs.x(), lhs.y() - rhs.y()); -} - -inline PointF PointAtOffsetFromOrigin(const Vector2dF& offset_from_origin) { - return PointF(offset_from_origin.x(), offset_from_origin.y()); -} - -GFX_EXPORT PointF ScalePoint(const PointF& p, float x_scale, float y_scale); - -inline PointF ScalePoint(const PointF& p, float scale) { - return ScalePoint(p, scale, scale); -} - -#if !defined(COMPILER_MSVC) -extern template class PointBase<PointF, float, Vector2dF>; -#endif - -} // namespace gfx - -#endif // UI_GFX_POINT_F_H_ diff --git a/chromium/ui/gfx/quad_f.h b/chromium/ui/gfx/quad_f.h index b8a42e9883e..05bf9d82dfc 100644 --- a/chromium/ui/gfx/quad_f.h +++ b/chromium/ui/gfx/quad_f.h @@ -1,109 +1,7 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_GFX_QUAD_F_H_ -#define UI_GFX_QUAD_F_H_ +// TODO(beng): remove once callsites are patched. +#include "ui/gfx/geometry/quad_f.h" -#include <algorithm> -#include <cmath> -#include <string> - -#include "ui/gfx/gfx_export.h" -#include "ui/gfx/point_f.h" -#include "ui/gfx/rect_f.h" - -namespace gfx { - -// A Quad is defined by four corners, allowing it to have edges that are not -// axis-aligned, unlike a Rect. -class GFX_EXPORT QuadF { - public: - QuadF() {} - QuadF(const PointF& p1, const PointF& p2, const PointF& p3, const PointF& p4) - : p1_(p1), - p2_(p2), - p3_(p3), - p4_(p4) {} - - explicit QuadF(const RectF& rect) - : p1_(rect.x(), rect.y()), - p2_(rect.right(), rect.y()), - p3_(rect.right(), rect.bottom()), - p4_(rect.x(), rect.bottom()) {} - - void operator=(const RectF& rect); - - void set_p1(const PointF& p) { p1_ = p; } - void set_p2(const PointF& p) { p2_ = p; } - void set_p3(const PointF& p) { p3_ = p; } - void set_p4(const PointF& p) { p4_ = p; } - - const PointF& p1() const { return p1_; } - const PointF& p2() const { return p2_; } - const PointF& p3() const { return p3_; } - const PointF& p4() const { return p4_; } - - // Returns true if the quad is an axis-aligned rectangle. - bool IsRectilinear() const; - - // Returns true if the points of the quad are in counter-clockwise order. This - // assumes that the quad is convex, and that no three points are collinear. - bool IsCounterClockwise() const; - - // Returns true if the |point| is contained within the quad, or lies on on - // edge of the quad. - bool Contains(const gfx::PointF& point) const; - - // Returns a rectangle that bounds the four points of the quad. The points of - // the quad may lie on the right/bottom edge of the resulting rectangle, - // rather than being strictly inside it. - RectF BoundingBox() const { - float rl = std::min(std::min(p1_.x(), p2_.x()), std::min(p3_.x(), p4_.x())); - float rr = std::max(std::max(p1_.x(), p2_.x()), std::max(p3_.x(), p4_.x())); - float rt = std::min(std::min(p1_.y(), p2_.y()), std::min(p3_.y(), p4_.y())); - float rb = std::max(std::max(p1_.y(), p2_.y()), std::max(p3_.y(), p4_.y())); - return RectF(rl, rt, rr - rl, rb - rt); - } - - // Add a vector to the quad, offseting each point in the quad by the vector. - void operator+=(const Vector2dF& rhs); - // Subtract a vector from the quad, offseting each point in the quad by the - // inverse of the vector. - void operator-=(const Vector2dF& rhs); - - // Scale each point in the quad by the |scale| factor. - void Scale(float scale) { Scale(scale, scale); } - - // Scale each point in the quad by the scale factors along each axis. - void Scale(float x_scale, float y_scale); - - // Returns a string representation of quad. - std::string ToString() const; - - private: - PointF p1_; - PointF p2_; - PointF p3_; - PointF p4_; -}; - -inline bool operator==(const QuadF& lhs, const QuadF& rhs) { - return - lhs.p1() == rhs.p1() && lhs.p2() == rhs.p2() && - lhs.p3() == rhs.p3() && lhs.p4() == rhs.p4(); -} - -inline bool operator!=(const QuadF& lhs, const QuadF& rhs) { - return !(lhs == rhs); -} - -// Add a vector to a quad, offseting each point in the quad by the vector. -GFX_EXPORT QuadF operator+(const QuadF& lhs, const Vector2dF& rhs); -// Subtract a vector from a quad, offseting each point in the quad by the -// inverse of the vector. -GFX_EXPORT QuadF operator-(const QuadF& lhs, const Vector2dF& rhs); - -} // namespace gfx - -#endif // UI_GFX_QUAD_F_H_ diff --git a/chromium/ui/gfx/rect.h b/chromium/ui/gfx/rect.h index df7f9aac366..a83d6b42cef 100644 --- a/chromium/ui/gfx/rect.h +++ b/chromium/ui/gfx/rect.h @@ -1,145 +1,7 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Defines a simple integer rectangle class. The containment semantics -// are array-like; that is, the coordinate (x, y) is considered to be -// contained by the rectangle, but the coordinate (x + width, y) is not. -// The class will happily let you create malformed rectangles (that is, -// rectangles with negative width and/or height), but there will be assertions -// in the operations (such as Contains()) to complain in this case. +// TODO(beng): remove once callsites are patched. +#include "ui/gfx/geometry/rect.h" -#ifndef UI_GFX_RECT_H_ -#define UI_GFX_RECT_H_ - -#include <cmath> -#include <string> - -#include "ui/gfx/point.h" -#include "ui/gfx/rect_base.h" -#include "ui/gfx/rect_f.h" -#include "ui/gfx/size.h" -#include "ui/gfx/vector2d.h" - -#if defined(OS_WIN) -typedef struct tagRECT RECT; -#elif defined(TOOLKIT_GTK) -typedef struct _GdkRectangle GdkRectangle; -#elif defined(OS_IOS) -#include <CoreGraphics/CoreGraphics.h> -#elif defined(OS_MACOSX) -#include <ApplicationServices/ApplicationServices.h> -#endif - -namespace gfx { - -class Insets; - -class GFX_EXPORT Rect - : public RectBase<Rect, Point, Size, Insets, Vector2d, int> { - public: - Rect() : RectBase<Rect, Point, Size, Insets, Vector2d, int>(Point()) {} - - Rect(int width, int height) - : RectBase<Rect, Point, Size, Insets, Vector2d, int> - (Size(width, height)) {} - - Rect(int x, int y, int width, int height) - : RectBase<Rect, Point, Size, Insets, Vector2d, int> - (Point(x, y), Size(width, height)) {} - -#if defined(OS_WIN) - explicit Rect(const RECT& r); -#elif defined(OS_MACOSX) - explicit Rect(const CGRect& r); -#elif defined(TOOLKIT_GTK) - explicit Rect(const GdkRectangle& r); -#endif - - explicit Rect(const gfx::Size& size) - : RectBase<Rect, Point, Size, Insets, Vector2d, int>(size) {} - - Rect(const gfx::Point& origin, const gfx::Size& size) - : RectBase<Rect, Point, Size, Insets, Vector2d, int>(origin, size) {} - - ~Rect() {} - -#if defined(OS_WIN) - // Construct an equivalent Win32 RECT object. - RECT ToRECT() const; -#elif defined(TOOLKIT_GTK) - GdkRectangle ToGdkRectangle() const; -#elif defined(OS_MACOSX) - // Construct an equivalent CoreGraphics object. - CGRect ToCGRect() const; -#endif - - operator RectF() const { - return RectF(origin().x(), origin().y(), size().width(), size().height()); - } - - std::string ToString() const; -}; - -inline bool operator==(const Rect& lhs, const Rect& rhs) { - return lhs.origin() == rhs.origin() && lhs.size() == rhs.size(); -} - -inline bool operator!=(const Rect& lhs, const Rect& rhs) { - return !(lhs == rhs); -} - -GFX_EXPORT Rect operator+(const Rect& lhs, const Vector2d& rhs); -GFX_EXPORT Rect operator-(const Rect& lhs, const Vector2d& rhs); - -inline Rect operator+(const Vector2d& lhs, const Rect& rhs) { - return rhs + lhs; -} - -GFX_EXPORT Rect IntersectRects(const Rect& a, const Rect& b); -GFX_EXPORT Rect UnionRects(const Rect& a, const Rect& b); -GFX_EXPORT Rect SubtractRects(const Rect& a, const Rect& b); - -// Constructs a rectangle with |p1| and |p2| as opposite corners. -// -// This could also be thought of as "the smallest rect that contains both -// points", except that we consider points on the right/bottom edges of the -// rect to be outside the rect. So technically one or both points will not be -// contained within the rect, because they will appear on one of these edges. -GFX_EXPORT Rect BoundingRect(const Point& p1, const Point& p2); - -inline Rect ScaleToEnclosingRect(const Rect& rect, - float x_scale, - float y_scale) { - int x = std::floor(rect.x() * x_scale); - int y = std::floor(rect.y() * y_scale); - int r = rect.width() == 0 ? x : std::ceil(rect.right() * x_scale); - int b = rect.height() == 0 ? y : std::ceil(rect.bottom() * y_scale); - return Rect(x, y, r - x, b - y); -} - -inline Rect ScaleToEnclosingRect(const Rect& rect, float scale) { - return ScaleToEnclosingRect(rect, scale, scale); -} - -inline Rect ScaleToEnclosedRect(const Rect& rect, - float x_scale, - float y_scale) { - int x = std::ceil(rect.x() * x_scale); - int y = std::ceil(rect.y() * y_scale); - int r = rect.width() == 0 ? x : std::floor(rect.right() * x_scale); - int b = rect.height() == 0 ? y : std::floor(rect.bottom() * y_scale); - return Rect(x, y, r - x, b - y); -} - -inline Rect ScaleToEnclosedRect(const Rect& rect, float scale) { - return ScaleToEnclosedRect(rect, scale, scale); -} - -#if !defined(COMPILER_MSVC) -extern template class RectBase<Rect, Point, Size, Insets, Vector2d, int>; -#endif - -} // namespace gfx - -#endif // UI_GFX_RECT_H_ diff --git a/chromium/ui/gfx/rect_conversions.h b/chromium/ui/gfx/rect_conversions.h index 988f6c76418..537435120c0 100644 --- a/chromium/ui/gfx/rect_conversions.h +++ b/chromium/ui/gfx/rect_conversions.h @@ -1,36 +1,7 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_GFX_RECT_CONVERSIONS_H_ -#define UI_GFX_RECT_CONVERSIONS_H_ +// TODO(beng): remove once callsites are patched. +#include "ui/gfx/geometry/rect_conversions.h" -#include "ui/gfx/rect.h" -#include "ui/gfx/rect_f.h" - -namespace gfx { - -// Returns the smallest Rect that encloses the given RectF. -GFX_EXPORT Rect ToEnclosingRect(const RectF& rect); - -// Returns the largest Rect that is enclosed by the given RectF. -GFX_EXPORT Rect ToEnclosedRect(const RectF& rect); - -// Returns the Rect after snapping the corners of the RectF to an integer grid. -// This should only be used when the RectF you provide is expected to be an -// integer rect with floating point error. If it is an arbitrary RectF, then -// you should use a different method. -GFX_EXPORT Rect ToNearestRect(const RectF& rect); - -// Returns true if the Rect produced after snapping the corners of the RectF -// to an integer grid is withing |distance|. -GFX_EXPORT bool IsNearestRectWithinDistance( - const gfx::RectF& rect, float distance); - -// Returns a Rect obtained by flooring the values of the given RectF. -// Please prefer the previous two functions in new code. -GFX_EXPORT Rect ToFlooredRectDeprecated(const RectF& rect); - -} // namespace gfx - -#endif // UI_GFX_RECT_CONVERSIONS_H_ diff --git a/chromium/ui/gfx/rect_f.h b/chromium/ui/gfx/rect_f.h index 09de529a627..71564bb65c8 100644 --- a/chromium/ui/gfx/rect_f.h +++ b/chromium/ui/gfx/rect_f.h @@ -1,113 +1,7 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_GFX_RECT_F_H_ -#define UI_GFX_RECT_F_H_ +// TODO(beng): remove once callsites are patched. +#include "ui/gfx/geometry/rect_f.h" -#include <string> - -#include "ui/gfx/point_f.h" -#include "ui/gfx/rect_base.h" -#include "ui/gfx/size_f.h" -#include "ui/gfx/vector2d_f.h" - -namespace gfx { - -class InsetsF; - -// A floating version of gfx::Rect. -class GFX_EXPORT RectF - : public RectBase<RectF, PointF, SizeF, InsetsF, Vector2dF, float> { - public: - RectF() - : RectBase<RectF, PointF, SizeF, InsetsF, Vector2dF, float> - (SizeF()) {} - - RectF(float width, float height) - : RectBase<RectF, PointF, SizeF, InsetsF, Vector2dF, float> - (SizeF(width, height)) {} - - RectF(float x, float y, float width, float height) - : RectBase<RectF, PointF, SizeF, InsetsF, Vector2dF, float> - (PointF(x, y), SizeF(width, height)) {} - - explicit RectF(const SizeF& size) - : RectBase<RectF, PointF, SizeF, InsetsF, Vector2dF, float> - (size) {} - - RectF(const PointF& origin, const SizeF& size) - : RectBase<RectF, PointF, SizeF, InsetsF, Vector2dF, float> - (origin, size) {} - - ~RectF() {} - - // Scales the rectangle by |scale|. - void Scale(float scale) { - Scale(scale, scale); - } - - void Scale(float x_scale, float y_scale) { - set_origin(ScalePoint(origin(), x_scale, y_scale)); - set_size(ScaleSize(size(), x_scale, y_scale)); - } - - // This method reports if the RectF can be safely converted to an integer - // Rect. When it is false, some dimension of the RectF is outside the bounds - // of what an integer can represent, and converting it to a Rect will require - // clamping. - bool IsExpressibleAsRect() const; - - std::string ToString() const; -}; - -inline bool operator==(const RectF& lhs, const RectF& rhs) { - return lhs.origin() == rhs.origin() && lhs.size() == rhs.size(); -} - -inline bool operator!=(const RectF& lhs, const RectF& rhs) { - return !(lhs == rhs); -} - -inline RectF operator+(const RectF& lhs, const Vector2dF& rhs) { - return RectF(lhs.x() + rhs.x(), lhs.y() + rhs.y(), - lhs.width(), lhs.height()); -} - -inline RectF operator-(const RectF& lhs, const Vector2dF& rhs) { - return RectF(lhs.x() - rhs.x(), lhs.y() - rhs.y(), - lhs.width(), lhs.height()); -} - -inline RectF operator+(const Vector2dF& lhs, const RectF& rhs) { - return rhs + lhs; -} - -GFX_EXPORT RectF IntersectRects(const RectF& a, const RectF& b); -GFX_EXPORT RectF UnionRects(const RectF& a, const RectF& b); -GFX_EXPORT RectF SubtractRects(const RectF& a, const RectF& b); - -inline RectF ScaleRect(const RectF& r, float x_scale, float y_scale) { - return RectF(r.x() * x_scale, r.y() * y_scale, - r.width() * x_scale, r.height() * y_scale); -} - -inline RectF ScaleRect(const RectF& r, float scale) { - return ScaleRect(r, scale, scale); -} - -// Constructs a rectangle with |p1| and |p2| as opposite corners. -// -// This could also be thought of as "the smallest rect that contains both -// points", except that we consider points on the right/bottom edges of the -// rect to be outside the rect. So technically one or both points will not be -// contained within the rect, because they will appear on one of these edges. -GFX_EXPORT RectF BoundingRect(const PointF& p1, const PointF& p2); - -#if !defined(COMPILER_MSVC) -extern template class RectBase<RectF, PointF, SizeF, InsetsF, Vector2dF, float>; -#endif - -} // namespace gfx - -#endif // UI_GFX_RECT_F_H_ diff --git a/chromium/ui/gfx/render_text.cc b/chromium/ui/gfx/render_text.cc index e8e3436f11c..c7871ee7a05 100644 --- a/chromium/ui/gfx/render_text.cc +++ b/chromium/ui/gfx/render_text.cc @@ -7,18 +7,24 @@ #include <algorithm> #include <climits> +#include "base/command_line.h" #include "base/i18n/break_iterator.h" #include "base/logging.h" #include "base/stl_util.h" +#include "base/strings/utf_string_conversions.h" #include "third_party/icu/source/common/unicode/rbbi.h" #include "third_party/icu/source/common/unicode/utf16.h" #include "third_party/skia/include/core/SkTypeface.h" #include "third_party/skia/include/effects/SkGradientShader.h" #include "ui/gfx/canvas.h" #include "ui/gfx/insets.h" +#include "ui/gfx/render_text_harfbuzz.h" +#include "ui/gfx/scoped_canvas.h" #include "ui/gfx/skia_util.h" +#include "ui/gfx/switches.h" #include "ui/gfx/text_constants.h" #include "ui/gfx/text_elider.h" +#include "ui/gfx/text_utils.h" #include "ui/gfx/utf16_indexing.h" namespace gfx { @@ -72,20 +78,20 @@ int DetermineBaselineCenteringText(const Rect& display_rect, return baseline + std::max(min_shift, std::min(max_shift, baseline_shift)); } -// Converts |gfx::Font::FontStyle| flags to |SkTypeface::Style| flags. +// Converts |Font::FontStyle| flags to |SkTypeface::Style| flags. SkTypeface::Style ConvertFontStyleToSkiaTypefaceStyle(int font_style) { int skia_style = SkTypeface::kNormal; - skia_style |= (font_style & gfx::Font::BOLD) ? SkTypeface::kBold : 0; - skia_style |= (font_style & gfx::Font::ITALIC) ? SkTypeface::kItalic : 0; + skia_style |= (font_style & Font::BOLD) ? SkTypeface::kBold : 0; + skia_style |= (font_style & Font::ITALIC) ? SkTypeface::kItalic : 0; return static_cast<SkTypeface::Style>(skia_style); } // Given |font| and |display_width|, returns the width of the fade gradient. -int CalculateFadeGradientWidth(const Font& font, int display_width) { +int CalculateFadeGradientWidth(const FontList& font_list, int display_width) { // Fade in/out about 2.5 characters of the beginning/end of the string. // The .5 here is helpful if one of the characters is a space. // Use a quarter of the display width if the display width is very short. - const int average_character_width = font.GetAverageCharacterWidth(); + const int average_character_width = font_list.GetExpectedTextWidth(1); const double gradient_width = std::min(average_character_width * 2.5, display_width / 4.0); DCHECK_GE(gradient_width, 0.0); @@ -158,7 +164,8 @@ namespace internal { const SkScalar kUnderlineMetricsNotSet = -1.0f; SkiaTextRenderer::SkiaTextRenderer(Canvas* canvas) - : canvas_skia_(canvas->sk_canvas()), + : canvas_(canvas), + canvas_skia_(canvas->sk_canvas()), started_drawing_(false), underline_thickness_(kUnderlineMetricsNotSet), underline_position_(0.0f) { @@ -168,6 +175,7 @@ SkiaTextRenderer::SkiaTextRenderer(Canvas* canvas) paint_.setAntiAlias(true); paint_.setSubpixelText(true); paint_.setLCDRenderText(true); + paint_.setHinting(SkPaint::kNormal_Hinting); bounds_.setEmpty(); } @@ -190,11 +198,16 @@ void SkiaTextRenderer::SetDrawLooper(SkDrawLooper* draw_looper) { paint_.setLooper(draw_looper); } -void SkiaTextRenderer::SetFontSmoothingSettings(bool enable_smoothing, - bool enable_lcd_text) { - paint_.setAntiAlias(enable_smoothing); - paint_.setSubpixelText(enable_smoothing); - paint_.setLCDRenderText(enable_lcd_text); +void SkiaTextRenderer::SetFontSmoothingSettings(bool antialiasing, + bool subpixel_rendering, + bool subpixel_positioning) { + paint_.setAntiAlias(antialiasing); + paint_.setLCDRenderText(subpixel_rendering); + paint_.setSubpixelText(subpixel_positioning); +} + +void SkiaTextRenderer::SetFontHinting(SkPaint::Hinting hinting) { + paint_.setHinting(hinting); } void SkiaTextRenderer::SetTypeface(SkTypeface* typeface) { @@ -209,17 +222,14 @@ void SkiaTextRenderer::SetFontFamilyWithStyle(const std::string& family, int style) { DCHECK(!family.empty()); - SkTypeface::Style skia_style = ConvertFontStyleToSkiaTypefaceStyle(style); - skia::RefPtr<SkTypeface> typeface = - skia::AdoptRef(SkTypeface::CreateFromName(family.c_str(), skia_style)); + skia::RefPtr<SkTypeface> typeface = CreateSkiaTypeface(family.c_str(), style); if (typeface) { // |paint_| adds its own ref. So don't |release()| it from the ref ptr here. SetTypeface(typeface.get()); // Enable fake bold text if bold style is needed but new typeface does not // have it. - paint_.setFakeBoldText((skia_style & SkTypeface::kBold) && - !typeface->isBold()); + paint_.setFakeBoldText((style & Font::BOLD) && !typeface->isBold()); } } @@ -271,8 +281,20 @@ void SkiaTextRenderer::DrawDecorations(int x, int y, int width, bool underline, DrawUnderline(x, y, width); if (strike) DrawStrike(x, y, width); - if (diagonal_strike) - DrawDiagonalStrike(x, y, width); + if (diagonal_strike) { + if (!diagonal_) + diagonal_.reset(new DiagonalStrike(canvas_, Point(x, y), paint_)); + diagonal_->AddPiece(width, paint_.getColor()); + } else if (diagonal_) { + EndDiagonalStrike(); + } +} + +void SkiaTextRenderer::EndDiagonalStrike() { + if (diagonal_) { + diagonal_->Draw(); + diagonal_.reset(); + } } void SkiaTextRenderer::DrawUnderline(int x, int y, int width) { @@ -294,15 +316,56 @@ void SkiaTextRenderer::DrawStrike(int x, int y, int width) const { canvas_skia_->drawRect(r, paint_); } -void SkiaTextRenderer::DrawDiagonalStrike(int x, int y, int width) const { +SkiaTextRenderer::DiagonalStrike::DiagonalStrike(Canvas* canvas, + Point start, + const SkPaint& paint) + : canvas_(canvas), + matrix_(canvas->sk_canvas()->getTotalMatrix()), + start_(start), + paint_(paint), + total_length_(0) { +} + +SkiaTextRenderer::DiagonalStrike::~DiagonalStrike() { +} + +void SkiaTextRenderer::DiagonalStrike::AddPiece(int length, SkColor color) { + pieces_.push_back(Piece(length, color)); + total_length_ += length; +} + +void SkiaTextRenderer::DiagonalStrike::Draw() { const SkScalar text_size = paint_.getTextSize(); const SkScalar offset = SkScalarMul(text_size, kDiagonalStrikeMarginOffset); + const int thickness = + SkScalarCeilToInt(SkScalarMul(text_size, kLineThickness) * 2); + const int height = SkScalarCeilToInt(text_size - offset); + const Point end = start_ + Vector2d(total_length_, -height); + const int clip_height = height + 2 * thickness; + + paint_.setAntiAlias(true); + paint_.setStrokeWidth(thickness); + + ScopedCanvas scoped_canvas(canvas_); + + SkCanvas* sk_canvas = canvas_->sk_canvas(); + sk_canvas->setMatrix(matrix_); + + const bool clipped = pieces_.size() > 1; + int x = start_.x(); + for (size_t i = 0; i < pieces_.size(); ++i) { + paint_.setColor(pieces_[i].second); + + if (clipped) { + sk_canvas->clipRect(RectToSkRect( + Rect(x, end.y() - thickness, pieces_[i].first, clip_height)), + SkRegion::kReplace_Op); + } + + canvas_->DrawLine(start_, end, paint_); - SkPaint paint(paint_); - paint.setAntiAlias(true); - paint.setStyle(SkPaint::kFill_Style); - paint.setStrokeWidth(SkScalarMul(text_size, kLineThickness) * 2); - canvas_skia_->drawLine(x, y, x + width, y - text_size + offset, paint); + x += pieces_[i].first; + } } StyleIterator::StyleIterator(const BreakList<SkColor>& colors, @@ -337,11 +400,30 @@ Line::Line() : preceding_heights(0), baseline(0) {} Line::~Line() {} +skia::RefPtr<SkTypeface> CreateSkiaTypeface(const std::string& family, + int style) { + SkTypeface::Style skia_style = ConvertFontStyleToSkiaTypefaceStyle(style); + return skia::AdoptRef(SkTypeface::CreateFromName(family.c_str(), skia_style)); +} + } // namespace internal RenderText::~RenderText() { } +RenderText* RenderText::CreateInstance() { +#if defined(OS_MACOSX) && defined(TOOLKIT_VIEWS) + // Use the more complete HarfBuzz implementation for Views controls on Mac. + return new RenderTextHarfBuzz; +#else + if (CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableHarfBuzzRenderText)) { + return new RenderTextHarfBuzz; + } + return CreateNativeInstance(); +#endif +} + void RenderText::SetText(const base::string16& text) { DCHECK(!composition_range_.IsValid()); if (text_ == text) @@ -365,7 +447,6 @@ void RenderText::SetText(const base::string16& text) { obscured_reveal_index_ = -1; UpdateLayoutText(); - ResetLayout(); } void RenderText::SetHorizontalAlignment(HorizontalAlignment alignment) { @@ -383,18 +464,6 @@ void RenderText::SetFontList(const FontList& font_list) { ResetLayout(); } -void RenderText::SetFont(const Font& font) { - SetFontList(FontList(font)); -} - -void RenderText::SetFontSize(int size) { - SetFontList(font_list_.DeriveFontListWithSize(size)); -} - -const Font& RenderText::GetPrimaryFont() const { - return font_list_.GetPrimaryFont(); -} - void RenderText::SetCursorEnabled(bool cursor_enabled) { cursor_enabled_ = cursor_enabled; cached_bounds_and_offset_valid_ = false; @@ -411,7 +480,6 @@ void RenderText::SetObscured(bool obscured) { obscured_reveal_index_ = -1; cached_bounds_and_offset_valid_ = false; UpdateLayoutText(); - ResetLayout(); } } @@ -422,7 +490,6 @@ void RenderText::SetObscuredRevealIndex(int index) { obscured_reveal_index_ = index; cached_bounds_and_offset_valid_ = false; UpdateLayoutText(); - ResetLayout(); } void RenderText::SetMultiline(bool multiline) { @@ -433,11 +500,23 @@ void RenderText::SetMultiline(bool multiline) { } } +void RenderText::SetElideBehavior(ElideBehavior elide_behavior) { + // TODO(skanuj) : Add a test for triggering layout change. + if (elide_behavior_ != elide_behavior) { + elide_behavior_ = elide_behavior; + UpdateLayoutText(); + } +} + void RenderText::SetDisplayRect(const Rect& r) { - display_rect_ = r; - baseline_ = kInvalidBaseline; - cached_bounds_and_offset_valid_ = false; - lines_.clear(); + if (r != display_rect_) { + display_rect_ = r; + baseline_ = kInvalidBaseline; + cached_bounds_and_offset_valid_ = false; + lines_.clear(); + if (elide_behavior_ != TRUNCATE) + UpdateLayoutText(); + } } void RenderText::SetCursorPosition(size_t position) { @@ -447,26 +526,28 @@ void RenderText::SetCursorPosition(size_t position) { void RenderText::MoveCursor(BreakType break_type, VisualCursorDirection direction, bool select) { - SelectionModel position(cursor_position(), selection_model_.caret_affinity()); + SelectionModel cursor(cursor_position(), selection_model_.caret_affinity()); // Cancelling a selection moves to the edge of the selection. if (break_type != LINE_BREAK && !selection().is_empty() && !select) { SelectionModel selection_start = GetSelectionModelForSelectionStart(); int start_x = GetCursorBounds(selection_start, true).x(); - int cursor_x = GetCursorBounds(position, true).x(); + int cursor_x = GetCursorBounds(cursor, true).x(); // Use the selection start if it is left (when |direction| is CURSOR_LEFT) // or right (when |direction| is CURSOR_RIGHT) of the selection end. if (direction == CURSOR_RIGHT ? start_x > cursor_x : start_x < cursor_x) - position = selection_start; - // For word breaks, use the nearest word boundary in the appropriate - // |direction|. + cursor = selection_start; + // Use the nearest word boundary in the proper |direction| for word breaks. if (break_type == WORD_BREAK) - position = GetAdjacentSelectionModel(position, break_type, direction); + cursor = GetAdjacentSelectionModel(cursor, break_type, direction); + // Use an adjacent selection model if the cursor is not at a valid position. + if (!IsValidCursorIndex(cursor.caret_pos())) + cursor = GetAdjacentSelectionModel(cursor, CHARACTER_BREAK, direction); } else { - position = GetAdjacentSelectionModel(position, break_type, direction); + cursor = GetAdjacentSelectionModel(cursor, break_type, direction); } if (select) - position.set_selection_start(selection().start()); - MoveCursorTo(position); + cursor.set_selection_start(selection().start()); + MoveCursorTo(cursor); } bool RenderText::MoveCursorTo(const SelectionModel& model) { @@ -474,9 +555,8 @@ bool RenderText::MoveCursorTo(const SelectionModel& model) { size_t text_length = text().length(); Range range(std::min(model.selection().start(), text_length), std::min(model.caret_pos(), text_length)); - // The current model only supports caret positions at valid character indices. - if (!IsCursorablePosition(range.start()) || - !IsCursorablePosition(range.end())) + // The current model only supports caret positions at valid cursor indices. + if (!IsValidCursorIndex(range.start()) || !IsValidCursorIndex(range.end())) return false; SelectionModel sel(range, model.caret_affinity()); bool changed = sel != selection_model_; @@ -484,17 +564,11 @@ bool RenderText::MoveCursorTo(const SelectionModel& model) { return changed; } -bool RenderText::MoveCursorTo(const Point& point, bool select) { - SelectionModel position = FindCursorPosition(point); - if (select) - position.set_selection_start(selection().start()); - return MoveCursorTo(position); -} - bool RenderText::SelectRange(const Range& range) { Range sel(std::min(range.start(), text().length()), std::min(range.end(), text().length())); - if (!IsCursorablePosition(sel.start()) || !IsCursorablePosition(sel.end())) + // Allow selection bounds at valid indicies amid multi-character graphemes. + if (!IsValidLogicalIndex(sel.start()) || !IsValidLogicalIndex(sel.end())) return false; LogicalCursorDirection affinity = (sel.is_reversed() || sel.is_empty()) ? CURSOR_FORWARD : CURSOR_BACKWARD; @@ -688,7 +762,7 @@ void RenderText::Draw(Canvas* canvas) { if (clip_to_display_rect()) { Rect clip_rect(display_rect()); - clip_rect.Inset(ShadowValue::GetMargin(text_shadows_)); + clip_rect.Inset(ShadowValue::GetMargin(shadows_)); canvas->Save(); canvas->ClipRect(clip_rect); @@ -713,27 +787,19 @@ void RenderText::DrawCursor(Canvas* canvas, const SelectionModel& position) { canvas->FillRect(GetCursorBounds(position, true), cursor_color_); } -void RenderText::DrawSelectedTextForDrag(Canvas* canvas) { - EnsureLayout(); - const std::vector<Rect> sel = GetSubstringBounds(selection()); - - // Override the selection color with black, and force the background to be - // transparent so that it's rendered without subpixel antialiasing. - const bool saved_background_is_transparent = background_is_transparent(); - const SkColor saved_selection_color = selection_color(); - set_background_is_transparent(true); - set_selection_color(SK_ColorBLACK); - - for (size_t i = 0; i < sel.size(); ++i) { - canvas->Save(); - canvas->ClipRect(sel[i]); - DrawVisualText(canvas); - canvas->Restore(); - } - - // Restore saved transparency and selection color. - set_selection_color(saved_selection_color); - set_background_is_transparent(saved_background_is_transparent); +bool RenderText::IsValidLogicalIndex(size_t index) { + // Check that the index is at a valid code point (not mid-surrgate-pair) and + // that it's not truncated from the layout text (its glyph may be shown). + // + // Indices within truncated text are disallowed so users can easily interact + // with the underlying truncated text using the ellipsis as a proxy. This lets + // users select all text, select the truncated text, and transition from the + // last rendered glyph to the end of the text without getting invisible cursor + // positions nor needing unbounded arrow key presses to traverse the ellipsis. + return index == 0 || index == text().length() || + (index < text().length() && + (truncate_length_ == 0 || index < truncate_length_) && + IsValidCodePointIndex(text(), index)); } Rect RenderText::GetCursorBounds(const SelectionModel& caret, @@ -743,9 +809,8 @@ Rect RenderText::GetCursorBounds(const SelectionModel& caret, // the multiline size, eliminate its use here. EnsureLayout(); - size_t caret_pos = caret.caret_pos(); - DCHECK(IsCursorablePosition(caret_pos)); + DCHECK(IsValidLogicalIndex(caret_pos)); // In overtype mode, ignore the affinity and always indicate that we will // overtype the next character. LogicalCursorDirection caret_affinity = @@ -786,7 +851,7 @@ size_t RenderText::IndexOfAdjacentGrapheme(size_t index, if (direction == CURSOR_FORWARD) { while (index < text().length()) { index++; - if (IsCursorablePosition(index)) + if (IsValidCursorIndex(index)) return index; } return text().length(); @@ -794,7 +859,7 @@ size_t RenderText::IndexOfAdjacentGrapheme(size_t index, while (index > 0) { index--; - if (IsCursorablePosition(index)) + if (IsValidCursorIndex(index)) return index; } return 0; @@ -808,10 +873,6 @@ SelectionModel RenderText::GetSelectionModelForSelectionStart() { sel.is_reversed() ? CURSOR_BACKWARD : CURSOR_FORWARD); } -void RenderText::SetTextShadows(const ShadowValues& shadows) { - text_shadows_ = shadows; -} - RenderText::RenderText() : horizontal_alignment_(base::i18n::IsRTL() ? ALIGN_RIGHT : ALIGN_LEFT), directionality_mode_(DIRECTIONALITY_FROM_TEXT), @@ -830,9 +891,8 @@ RenderText::RenderText() obscured_(false), obscured_reveal_index_(-1), truncate_length_(0), + elide_behavior_(TRUNCATE), multiline_(false), - fade_head_(false), - fade_tail_(false), background_is_transparent_(false), clip_to_display_rect_(true), baseline_(kInvalidBaseline), @@ -872,7 +932,7 @@ void RenderText::SetSelectionModel(const SelectionModel& model) { } const base::string16& RenderText::GetLayoutText() const { - return layout_text_.empty() ? text_ : layout_text_; + return layout_text_; } const BreakList<size_t>& RenderText::GetLineBreaks() { @@ -1013,38 +1073,23 @@ Vector2d RenderText::GetAlignmentOffset(size_t line_number) { } void RenderText::ApplyFadeEffects(internal::SkiaTextRenderer* renderer) { - if (multiline() || (!fade_head() && !fade_tail())) + const int width = display_rect().width(); + if (multiline() || elide_behavior_ != FADE_TAIL || GetContentWidth() <= width) return; - const int display_width = display_rect().width(); - - // If the text fits as-is, no need to fade. - if (GetStringSize().width() <= display_width) - return; - - int gradient_width = CalculateFadeGradientWidth(GetPrimaryFont(), - display_width); + const int gradient_width = CalculateFadeGradientWidth(font_list(), width); if (gradient_width == 0) return; - bool fade_left = fade_head(); - bool fade_right = fade_tail(); - // Under RTL, |fade_right| == |fade_head|. - // TODO(asvitkine): This is currently not based on GetTextDirection() because - // RenderTextWin does not return a direction that's based on - // the text content. - if (horizontal_alignment_ == ALIGN_RIGHT) - std::swap(fade_left, fade_right); - Rect solid_part = display_rect(); Rect left_part; Rect right_part; - if (fade_left) { + if (horizontal_alignment_ != ALIGN_LEFT) { left_part = solid_part; left_part.Inset(0, 0, solid_part.width() - gradient_width, 0); solid_part.Inset(gradient_width, 0, 0, 0); } - if (fade_right) { + if (horizontal_alignment_ != ALIGN_RIGHT) { right_part = solid_part; right_part.Inset(solid_part.width() - gradient_width, 0, 0, 0); solid_part.Inset(0, 0, gradient_width, 0); @@ -1061,7 +1106,7 @@ void RenderText::ApplyFadeEffects(internal::SkiaTextRenderer* renderer) { } void RenderText::ApplyTextShadows(internal::SkiaTextRenderer* renderer) { - skia::RefPtr<SkDrawLooper> looper = CreateShadowDrawLooper(text_shadows_); + skia::RefPtr<SkDrawLooper> looper = CreateShadowDrawLooper(shadows_); renderer->SetDrawLooper(looper.get()); } @@ -1077,7 +1122,7 @@ bool RenderText::RangeContainsCaret(const Range& range, void RenderText::MoveCursorTo(size_t position, bool select) { size_t cursor = std::min(position, text().length()); - if (IsCursorablePosition(cursor)) + if (IsValidCursorIndex(cursor)) SetSelectionModel(SelectionModel( Range(select ? selection().start() : cursor, cursor), (cursor == 0) ? CURSOR_FORWARD : CURSOR_BACKWARD)); @@ -1089,7 +1134,7 @@ void RenderText::UpdateLayoutText() { if (obscured_) { size_t obscured_text_length = - static_cast<size_t>(gfx::UTF16IndexToOffset(text_, 0, text_.length())); + static_cast<size_t>(UTF16IndexToOffset(text_, 0, text_.length())); layout_text_.assign(obscured_text_length, kPasswordReplacementChar); if (obscured_reveal_index_ >= 0 && @@ -1103,19 +1148,122 @@ void RenderText::UpdateLayoutText() { // Gets the index in |layout_text_| to be replaced. const size_t cp_start = - static_cast<size_t>(gfx::UTF16IndexToOffset(text_, 0, start)); + static_cast<size_t>(UTF16IndexToOffset(text_, 0, start)); if (layout_text_.length() > cp_start) layout_text_.replace(cp_start, 1, text_.substr(start, end - start)); } + } else { + layout_text_ = text_; } - const base::string16& text = obscured_ ? layout_text_ : text_; + const base::string16& text = layout_text_; if (truncate_length_ > 0 && truncate_length_ < text.length()) { // Truncate the text at a valid character break and append an ellipsis. icu::StringCharacterIterator iter(text.c_str()); iter.setIndex32(truncate_length_ - 1); - layout_text_.assign(text.substr(0, iter.getIndex()) + gfx::kEllipsisUTF16); + layout_text_.assign(text.substr(0, iter.getIndex()) + kEllipsisUTF16); + } + + if (elide_behavior_ != TRUNCATE && elide_behavior_ != FADE_TAIL && + display_rect_.width() > 0 && !layout_text_.empty() && + GetContentWidth() > display_rect_.width()) { + // This doesn't trim styles so ellipsis may get rendered as a different + // style than the preceding text. See crbug.com/327850. + layout_text_.assign(ElideText(layout_text_)); + } + + ResetLayout(); +} + +// TODO(skanuj): Fix code duplication with ElideText in ui/gfx/text_elider.cc +// See crbug.com/327846 +base::string16 RenderText::ElideText(const base::string16& text) { + const bool insert_ellipsis = (elide_behavior_ != TRUNCATE); + // Create a RenderText copy with attributes that affect the rendering width. + scoped_ptr<RenderText> render_text(CreateInstance()); + render_text->SetFontList(font_list_); + render_text->SetDirectionalityMode(directionality_mode_); + render_text->SetCursorEnabled(cursor_enabled_); + + render_text->styles_ = styles_; + render_text->colors_ = colors_; + render_text->SetText(text); + const int current_text_pixel_width = render_text->GetContentWidth(); + + const base::string16 ellipsis = base::string16(kEllipsisUTF16); + const bool elide_in_middle = false; + const bool elide_at_beginning = false; + StringSlicer slicer(text, ellipsis, elide_in_middle, elide_at_beginning); + + // Pango will return 0 width for absurdly long strings. Cut the string in + // half and try again. + // This is caused by an int overflow in Pango (specifically, in + // pango_glyph_string_extents_range). It's actually more subtle than just + // returning 0, since on super absurdly long strings, the int can wrap and + // return positive numbers again. Detecting that is probably not worth it + // (eliding way too much from a ridiculous string is probably still + // ridiculous), but we should check other widths for bogus values as well. + if (current_text_pixel_width <= 0 && !text.empty()) + return ElideText(slicer.CutString(text.length() / 2, insert_ellipsis)); + + if (current_text_pixel_width <= display_rect_.width()) + return text; + + render_text->SetText(base::string16()); + render_text->SetText(ellipsis); + const int ellipsis_width = render_text->GetContentWidth(); + + if (insert_ellipsis && (ellipsis_width >= display_rect_.width())) + return base::string16(); + + // Use binary search to compute the elided text. + size_t lo = 0; + size_t hi = text.length() - 1; + const base::i18n::TextDirection text_direction = GetTextDirection(); + for (size_t guess = (lo + hi) / 2; lo <= hi; guess = (lo + hi) / 2) { + // Restore styles and colors. They will be truncated to size by SetText. + render_text->styles_ = styles_; + render_text->colors_ = colors_; + base::string16 new_text = slicer.CutString(guess, false); + render_text->SetText(new_text); + + // This has to be an additional step so that the ellipsis is rendered with + // same style as trailing part of the text. + if (insert_ellipsis) { + // When ellipsis follows text whose directionality is not the same as that + // of the whole text, it will be rendered with the directionality of the + // whole text. Since we want ellipsis to indicate continuation of the + // preceding text, we force the directionality of ellipsis to be same as + // the preceding text using LTR or RTL markers. + base::i18n::TextDirection trailing_text_direction = + base::i18n::GetLastStrongCharacterDirection(new_text); + new_text.append(ellipsis); + if (trailing_text_direction != text_direction) { + if (trailing_text_direction == base::i18n::LEFT_TO_RIGHT) + new_text += base::i18n::kLeftToRightMark; + else + new_text += base::i18n::kRightToLeftMark; + } + render_text->SetText(new_text); + } + + // We check the width of the whole desired string at once to ensure we + // handle kerning/ligatures/etc. correctly. + const int guess_width = render_text->GetContentWidth(); + if (guess_width == display_rect_.width()) + break; + if (guess_width > display_rect_.width()) { + hi = guess - 1; + // Move back if we are on loop terminating condition, and guess is wider + // than available. + if (hi < lo) + lo = hi; + } else { + lo = guess + 1; + } } + + return render_text->text(); } void RenderText::UpdateCachedBoundsAndOffset() { @@ -1128,7 +1276,8 @@ void RenderText::UpdateCachedBoundsAndOffset() { // the stale |display_offset_|. Applying |delta_offset| at the end of this // function will set |cursor_bounds_| and |display_offset_| to correct values. cached_bounds_and_offset_valid_ = true; - cursor_bounds_ = GetCursorBounds(selection_model_, insert_mode_); + if (cursor_enabled()) + cursor_bounds_ = GetCursorBounds(selection_model_, insert_mode_); // Update |display_offset_| to ensure the current cursor is visible. const int display_width = display_rect_.width(); diff --git a/chromium/ui/gfx/render_text.h b/chromium/ui/gfx/render_text.h index 8696d690560..885df0a9b41 100644 --- a/chromium/ui/gfx/render_text.h +++ b/chromium/ui/gfx/render_text.h @@ -13,6 +13,7 @@ #include "base/gtest_prod_util.h" #include "base/i18n/rtl.h" +#include "base/memory/scoped_ptr.h" #include "base/strings/string16.h" #include "skia/ext/refptr.h" #include "third_party/skia/include/core/SkColor.h" @@ -27,6 +28,7 @@ #include "ui/gfx/shadow_value.h" #include "ui/gfx/size_f.h" #include "ui/gfx/text_constants.h" +#include "ui/gfx/text_elider.h" #include "ui/gfx/vector2d.h" class SkCanvas; @@ -50,7 +52,10 @@ class SkiaTextRenderer { ~SkiaTextRenderer(); void SetDrawLooper(SkDrawLooper* draw_looper); - void SetFontSmoothingSettings(bool enable_smoothing, bool enable_lcd_text); + void SetFontSmoothingSettings(bool antialiasing, + bool subpixel_rendering, + bool subpixel_positioning); + void SetFontHinting(SkPaint::Hinting hinting); void SetTypeface(SkTypeface* typeface); void SetTextSize(SkScalar size); void SetFontFamilyWithStyle(const std::string& family, int font_style); @@ -69,11 +74,36 @@ class SkiaTextRenderer { // third_party/skia/src/core/SkTextFormatParams.h void DrawDecorations(int x, int y, int width, bool underline, bool strike, bool diagonal_strike); + // Finishes any ongoing diagonal strike run. + void EndDiagonalStrike(); void DrawUnderline(int x, int y, int width); void DrawStrike(int x, int y, int width) const; - void DrawDiagonalStrike(int x, int y, int width) const; private: + // Helper class to draw a diagonal line with multiple pieces of different + // lengths and colors; to support text selection appearances. + class DiagonalStrike { + public: + DiagonalStrike(Canvas* canvas, Point start, const SkPaint& paint); + ~DiagonalStrike(); + + void AddPiece(int length, SkColor color); + void Draw(); + + private: + typedef std::pair<int, SkColor> Piece; + + Canvas* canvas_; + SkMatrix matrix_; + const Point start_; + SkPaint paint_; + int total_length_; + std::vector<Piece> pieces_; + + DISALLOW_COPY_AND_ASSIGN(DiagonalStrike); + }; + + Canvas* canvas_; SkCanvas* canvas_skia_; bool started_drawing_; SkPaint paint_; @@ -81,6 +111,7 @@ class SkiaTextRenderer { skia::RefPtr<SkShader> deferred_fade_shader_; SkScalar underline_thickness_; SkScalar underline_position_; + scoped_ptr<DiagonalStrike> diagonal_; DISALLOW_COPY_AND_ASSIGN(SkiaTextRenderer); }; @@ -146,6 +177,10 @@ struct Line { int baseline; }; +// Creates an SkTypeface from a font |family| name and a |gfx::Font::FontStyle|. +skia::RefPtr<SkTypeface> CreateSkiaTypeface(const std::string& family, + int style); + } // namespace internal // RenderText represents an abstract model of styled text and its corresponding @@ -156,7 +191,7 @@ class GFX_EXPORT RenderText { public: virtual ~RenderText(); - // Creates a platform-specific RenderText instance. + // Creates a platform-specific or cross-platform RenderText instance. static RenderText* CreateInstance(); const base::string16& text() const { return text_; } @@ -169,13 +204,6 @@ class GFX_EXPORT RenderText { const FontList& font_list() const { return font_list_; } void SetFontList(const FontList& font_list); - void SetFont(const Font& font); - - // Set the font size to |size| in pixels. - void SetFontSize(int size); - - // Get the first font in |font_list_|. - const Font& GetPrimaryFont() const; bool cursor_enabled() const { return cursor_enabled_; } void SetCursorEnabled(bool cursor_enabled); @@ -227,14 +255,14 @@ class GFX_EXPORT RenderText { // WARNING: Only use this for system limits, it lacks complex text support. void set_truncate_length(size_t length) { truncate_length_ = length; } + // Elides the text to fit in |display_rect| according to the specified + // |elide_behavior|. |ELIDE_MIDDLE| is not supported. If a truncate length and + // an elide mode are specified, the shorter of the two will be applicable. + void SetElideBehavior(ElideBehavior elide_behavior); + const Rect& display_rect() const { return display_rect_; } void SetDisplayRect(const Rect& r); - void set_fade_head(bool fade_head) { fade_head_ = fade_head; } - bool fade_head() const { return fade_head_; } - void set_fade_tail(bool fade_tail) { fade_tail_ = fade_tail; } - bool fade_tail() const { return fade_tail_; } - bool background_is_transparent() const { return background_is_transparent_; } void set_background_is_transparent(bool transparent) { background_is_transparent_ = transparent; @@ -261,11 +289,6 @@ class GFX_EXPORT RenderText { // grapheme boundary), it is a no-op and returns false. bool MoveCursorTo(const SelectionModel& selection_model); - // Move the cursor to the position associated with the clicked point. - // If |select| is false, the selection start is moved to the same position. - // Returns true if the cursor position or selection range changed. - bool MoveCursorTo(const Point& point, bool select); - // Set the selection_model_ based on |range|. // If the |range| start or end is greater than text length, it is modified // to be the text length. @@ -320,9 +343,8 @@ class GFX_EXPORT RenderText { VisualCursorDirection GetVisualDirectionOfLogicalEnd(); // Returns the size required to display the current string (which is the - // wrapped size in multiline mode). Note that this returns the raw size of the - // string, which does not include the cursor or the margin area of text - // shadows. + // wrapped size in multiline mode). The returned size does not include space + // reserved for the cursor or the offset text shadows. virtual Size GetStringSize() = 0; // This is same as GetStringSize except that fractional size is returned. @@ -347,16 +369,16 @@ class GFX_EXPORT RenderText { // Draws a cursor at |position|. void DrawCursor(Canvas* canvas, const SelectionModel& position); - // Draw the selected text without a cursor or selection highlight. Subpixel - // antialiasing is disabled and foreground color is forced to black. - void DrawSelectedTextForDrag(Canvas* canvas); - // Gets the SelectionModel from a visual point in local coordinates. virtual SelectionModel FindCursorPosition(const Point& point) = 0; - // Return true if cursor can appear in front of the character at |position|, - // which means it is a grapheme boundary or the first character in the text. - virtual bool IsCursorablePosition(size_t position) = 0; + // Returns true if the position is a valid logical index into text(), and is + // also a valid grapheme boundary, which may be used as a cursor position. + virtual bool IsValidCursorIndex(size_t index) = 0; + + // Returns true if the position is a valid logical index into text(). Indices + // amid multi-character graphemes are allowed here, unlike IsValidCursorIndex. + virtual bool IsValidLogicalIndex(size_t index); // Get the visual bounds of a cursor at |caret|. These bounds typically // represent a vertical line if |insert_mode| is true. Pass false for @@ -364,8 +386,7 @@ class GFX_EXPORT RenderText { // are in local coordinates, but may be outside the visible region if the text // is longer than the textfield. Subsequent text, cursor, or bounds changes // may invalidate returned values. Note that |caret| must be placed at - // grapheme boundary, that is, |IsCursorablePosition(caret.caret_pos())| must - // return true. + // grapheme boundary, i.e. caret.caret_pos() must be a cursorable position. Rect GetCursorBounds(const SelectionModel& caret, bool insert_mode); // Compute the current cursor bounds, panning the text to show the cursor in @@ -374,10 +395,9 @@ class GFX_EXPORT RenderText { const Rect& GetUpdatedCursorBounds(); // Given an |index| in text(), return the next or previous grapheme boundary - // in logical order (that is, the nearest index for which - // |IsCursorablePosition(index)| returns true). The return value is in the - // range 0 to text().length() inclusive (the input is clamped if it is out of - // that range). Always moves by at least one character index unless the + // in logical order (i.e. the nearest cursorable index). The return value is + // in the range 0 to text().length() inclusive (the input is clamped if it is + // out of that range). Always moves by at least one character index unless the // supplied index is already at the boundary of the string. size_t IndexOfAdjacentGrapheme(size_t index, LogicalCursorDirection direction); @@ -387,7 +407,7 @@ class GFX_EXPORT RenderText { SelectionModel GetSelectionModelForSelectionStart(); // Sets shadows to drawn with text. - void SetTextShadows(const ShadowValues& shadows); + void set_shadows(const ShadowValues& shadows) { shadows_ = shadows; } typedef std::pair<Font, Range> FontSpan; // For testing purposes, returns which fonts were chosen for which parts of @@ -396,6 +416,12 @@ class GFX_EXPORT RenderText { // chosen. virtual std::vector<FontSpan> GetFontSpansForTesting() = 0; + // Gets the horizontal bounds (relative to the left of the text, not the view) + // of the glyph starting at |index|. If the glyph is RTL then the returned + // Range will have is_reversed() true. (This does not return a Rect because a + // Rect can't have a negative width.) + virtual Range GetGlyphBounds(size_t index) = 0; + protected: RenderText(); @@ -460,12 +486,6 @@ class GFX_EXPORT RenderText { // Sets the selection model, the argument is assumed to be valid. virtual void SetSelectionModel(const SelectionModel& model); - // Get the horizontal bounds (relative to the left of the text, not the view) - // of the glyph starting at |index|. If the glyph is RTL then the returned - // Range will have is_reversed() true. (This does not return a Rect because a - // Rect can't have a negative width.) - virtual Range GetGlyphBounds(size_t index) = 0; - // Get the visual bounds containing the logical substring within the |range|. // If |range| is empty, the result is empty. These bounds could be visually // discontinuous if the substring is split by a LTR/RTL level change. @@ -508,12 +528,10 @@ class GFX_EXPORT RenderText { Point ToTextPoint(const Point& point); Point ToViewPoint(const Point& point); - // Convert a text space x-coordinate range to corresponding rects in view - // space. + // Convert a text space x-coordinate range to rects in view space. std::vector<Rect> TextBoundsToViewBounds(const Range& x); - // Returns the line offset from the origin, accounting for text alignment - // only. + // Returns the line offset from the origin, accounts for text alignment only. Vector2d GetAlignmentOffset(size_t line_number); // Applies fade effects to |renderer|. @@ -535,6 +553,8 @@ class GFX_EXPORT RenderText { FRIEND_TEST_ALL_PREFIXES(RenderTextTest, ApplyColorAndStyle); FRIEND_TEST_ALL_PREFIXES(RenderTextTest, ObscuredText); FRIEND_TEST_ALL_PREFIXES(RenderTextTest, RevealObscuredText); + FRIEND_TEST_ALL_PREFIXES(RenderTextTest, ElidedText); + FRIEND_TEST_ALL_PREFIXES(RenderTextTest, ElidedObscuredText); FRIEND_TEST_ALL_PREFIXES(RenderTextTest, TruncatedText); FRIEND_TEST_ALL_PREFIXES(RenderTextTest, TruncatedObscuredText); FRIEND_TEST_ALL_PREFIXES(RenderTextTest, GraphemePositions); @@ -545,6 +565,11 @@ class GFX_EXPORT RenderText { FRIEND_TEST_ALL_PREFIXES(RenderTextTest, Multiline_NormalWidth); FRIEND_TEST_ALL_PREFIXES(RenderTextTest, Multiline_SufficientWidth); FRIEND_TEST_ALL_PREFIXES(RenderTextTest, Multiline_Newline); + FRIEND_TEST_ALL_PREFIXES(RenderTextTest, GlyphBounds); + FRIEND_TEST_ALL_PREFIXES(RenderTextTest, HarfBuzz_GlyphBounds); + + // Creates a platform-specific RenderText instance. + static RenderText* CreateNativeInstance(); // Set the cursor to |position|, with the caret trailing the previous // grapheme, or if there is no previous grapheme, leading the cursor position. @@ -556,6 +581,10 @@ class GFX_EXPORT RenderText { // Updates |layout_text_| if the text is obscured or truncated. void UpdateLayoutText(); + // Elides |text| to fit in the |display_rect_| with given |elide_behavior_|. + // See ElideText in ui/gfx/text_elider.cc for reference. + base::string16 ElideText(const base::string16& text); + // Update the cached bounds and display offset to ensure that the current // cursor is within the visible display area. void UpdateCachedBoundsAndOffset(); @@ -628,6 +657,9 @@ class GFX_EXPORT RenderText { // The maximum length of text to display, 0 forgoes a hard limit. size_t truncate_length_; + // The behavior for eliding, fading, or truncating. + ElideBehavior elide_behavior_; + // The obscured and/or truncated text that will be displayed. base::string16 layout_text_; @@ -635,10 +667,6 @@ class GFX_EXPORT RenderText { // |display_rect_| as the width cap. bool multiline_; - // Fade text head and/or tail, if text doesn't fit into |display_rect_|. - bool fade_head_; - bool fade_tail_; - // Is the background transparent (either partially or fully)? bool background_is_transparent_; @@ -665,7 +693,7 @@ class GFX_EXPORT RenderText { bool cached_bounds_and_offset_valid_; // Text shadows to be drawn. - ShadowValues text_shadows_; + ShadowValues shadows_; // A list of valid layout text line break positions. BreakList<size_t> line_breaks_; diff --git a/chromium/ui/gfx/render_text_harfbuzz.cc b/chromium/ui/gfx/render_text_harfbuzz.cc new file mode 100644 index 00000000000..3d9bf58141e --- /dev/null +++ b/chromium/ui/gfx/render_text_harfbuzz.cc @@ -0,0 +1,1010 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/gfx/render_text_harfbuzz.h" + +#include <map> + +#include "base/debug/leak_annotations.h" +#include "base/i18n/bidi_line_iterator.h" +#include "base/i18n/break_iterator.h" +#include "base/i18n/char_iterator.h" +#include "base/lazy_instance.h" +#include "third_party/harfbuzz-ng/src/hb.h" +#include "third_party/icu/source/common/unicode/ubidi.h" +#include "third_party/skia/include/core/SkColor.h" +#include "third_party/skia/include/core/SkTypeface.h" +#include "ui/gfx/canvas.h" +#include "ui/gfx/utf16_indexing.h" + +#if defined(OS_WIN) +#include "ui/gfx/font_smoothing_win.h" +#endif + +namespace gfx { + +namespace { + +// The maximum number of scripts a Unicode character can belong to. This value +// is arbitrarily chosen to be a good limit because it is unlikely for a single +// character to belong to more scripts. +const size_t kMaxScripts = 5; + +// Maps from code points to glyph indices in a font. +typedef std::map<uint32_t, uint16_t> GlyphCache; + +// Font data provider for HarfBuzz using Skia. Copied from Blink. +// TODO(ckocagil): Eliminate the duplication. http://crbug.com/368375 +struct FontData { + FontData(GlyphCache* glyph_cache) : glyph_cache_(glyph_cache) {} + + SkPaint paint_; + GlyphCache* glyph_cache_; +}; + +hb_position_t SkiaScalarToHarfBuzzPosition(SkScalar value) { + return SkScalarToFixed(value); +} + +// Deletes the object at the given pointer after casting it to the given type. +template<typename Type> +void DeleteByType(void* data) { + Type* typed_data = reinterpret_cast<Type*>(data); + delete typed_data; +} + +template<typename Type> +void DeleteArrayByType(void* data) { + Type* typed_data = reinterpret_cast<Type*>(data); + delete[] typed_data; +} + +// Outputs the |width| and |extents| of the glyph with index |codepoint| in +// |paint|'s font. +void GetGlyphWidthAndExtents(SkPaint* paint, + hb_codepoint_t codepoint, + hb_position_t* width, + hb_glyph_extents_t* extents) { + DCHECK_LE(codepoint, 0xFFFFU); + paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding); + + SkScalar sk_width; + SkRect sk_bounds; + uint16_t glyph = codepoint; + + paint->getTextWidths(&glyph, sizeof(glyph), &sk_width, &sk_bounds); + if (width) + *width = SkiaScalarToHarfBuzzPosition(sk_width); + if (extents) { + // Invert y-axis because Skia is y-grows-down but we set up HarfBuzz to be + // y-grows-up. + extents->x_bearing = SkiaScalarToHarfBuzzPosition(sk_bounds.fLeft); + extents->y_bearing = SkiaScalarToHarfBuzzPosition(-sk_bounds.fTop); + extents->width = SkiaScalarToHarfBuzzPosition(sk_bounds.width()); + extents->height = SkiaScalarToHarfBuzzPosition(-sk_bounds.height()); + } +} + +// Writes the |glyph| index for the given |unicode| code point. Returns whether +// the glyph exists, i.e. it is not a missing glyph. +hb_bool_t GetGlyph(hb_font_t* font, + void* data, + hb_codepoint_t unicode, + hb_codepoint_t variation_selector, + hb_codepoint_t* glyph, + void* user_data) { + FontData* font_data = reinterpret_cast<FontData*>(data); + GlyphCache* cache = font_data->glyph_cache_; + + bool exists = cache->count(unicode) != 0; + if (!exists) { + SkPaint* paint = &font_data->paint_; + paint->setTextEncoding(SkPaint::kUTF32_TextEncoding); + paint->textToGlyphs(&unicode, sizeof(hb_codepoint_t), &(*cache)[unicode]); + } + *glyph = (*cache)[unicode]; + return !!*glyph; +} + +// Returns the horizontal advance value of the |glyph|. +hb_position_t GetGlyphHorizontalAdvance(hb_font_t* font, + void* data, + hb_codepoint_t glyph, + void* user_data) { + FontData* font_data = reinterpret_cast<FontData*>(data); + hb_position_t advance = 0; + + GetGlyphWidthAndExtents(&font_data->paint_, glyph, &advance, 0); + return advance; +} + +hb_bool_t GetGlyphHorizontalOrigin(hb_font_t* font, + void* data, + hb_codepoint_t glyph, + hb_position_t* x, + hb_position_t* y, + void* user_data) { + // Just return true, like the HarfBuzz-FreeType implementation. + return true; +} + +hb_position_t GetGlyphKerning(FontData* font_data, + hb_codepoint_t first_glyph, + hb_codepoint_t second_glyph) { + SkTypeface* typeface = font_data->paint_.getTypeface(); + const uint16_t glyphs[2] = { static_cast<uint16_t>(first_glyph), + static_cast<uint16_t>(second_glyph) }; + int32_t kerning_adjustments[1] = { 0 }; + + if (!typeface->getKerningPairAdjustments(glyphs, 2, kerning_adjustments)) + return 0; + + SkScalar upm = SkIntToScalar(typeface->getUnitsPerEm()); + SkScalar size = font_data->paint_.getTextSize(); + return SkiaScalarToHarfBuzzPosition( + SkScalarMulDiv(SkIntToScalar(kerning_adjustments[0]), size, upm)); +} + +hb_position_t GetGlyphHorizontalKerning(hb_font_t* font, + void* data, + hb_codepoint_t left_glyph, + hb_codepoint_t right_glyph, + void* user_data) { + FontData* font_data = reinterpret_cast<FontData*>(data); + if (font_data->paint_.isVerticalText()) { + // We don't support cross-stream kerning. + return 0; + } + + return GetGlyphKerning(font_data, left_glyph, right_glyph); +} + +hb_position_t GetGlyphVerticalKerning(hb_font_t* font, + void* data, + hb_codepoint_t top_glyph, + hb_codepoint_t bottom_glyph, + void* user_data) { + FontData* font_data = reinterpret_cast<FontData*>(data); + if (!font_data->paint_.isVerticalText()) { + // We don't support cross-stream kerning. + return 0; + } + + return GetGlyphKerning(font_data, top_glyph, bottom_glyph); +} + +// Writes the |extents| of |glyph|. +hb_bool_t GetGlyphExtents(hb_font_t* font, + void* data, + hb_codepoint_t glyph, + hb_glyph_extents_t* extents, + void* user_data) { + FontData* font_data = reinterpret_cast<FontData*>(data); + + GetGlyphWidthAndExtents(&font_data->paint_, glyph, 0, extents); + return true; +} + +class FontFuncs { + public: + FontFuncs() : font_funcs_(hb_font_funcs_create()) { + hb_font_funcs_set_glyph_func(font_funcs_, GetGlyph, 0, 0); + hb_font_funcs_set_glyph_h_advance_func( + font_funcs_, GetGlyphHorizontalAdvance, 0, 0); + hb_font_funcs_set_glyph_h_kerning_func( + font_funcs_, GetGlyphHorizontalKerning, 0, 0); + hb_font_funcs_set_glyph_h_origin_func( + font_funcs_, GetGlyphHorizontalOrigin, 0, 0); + hb_font_funcs_set_glyph_v_kerning_func( + font_funcs_, GetGlyphVerticalKerning, 0, 0); + hb_font_funcs_set_glyph_extents_func( + font_funcs_, GetGlyphExtents, 0, 0); + hb_font_funcs_make_immutable(font_funcs_); + } + + ~FontFuncs() { + hb_font_funcs_destroy(font_funcs_); + } + + hb_font_funcs_t* get() { return font_funcs_; } + + private: + hb_font_funcs_t* font_funcs_; + + DISALLOW_COPY_AND_ASSIGN(FontFuncs); +}; + +base::LazyInstance<FontFuncs>::Leaky g_font_funcs = LAZY_INSTANCE_INITIALIZER; + +// Returns the raw data of the font table |tag|. +hb_blob_t* GetFontTable(hb_face_t* face, hb_tag_t tag, void* user_data) { + SkTypeface* typeface = reinterpret_cast<SkTypeface*>(user_data); + + const size_t table_size = typeface->getTableSize(tag); + if (!table_size) + return 0; + + scoped_ptr<char[]> buffer(new char[table_size]); + if (!buffer) + return 0; + size_t actual_size = typeface->getTableData(tag, 0, table_size, buffer.get()); + if (table_size != actual_size) + return 0; + + char* buffer_raw = buffer.release(); + return hb_blob_create(buffer_raw, table_size, HB_MEMORY_MODE_WRITABLE, + buffer_raw, DeleteArrayByType<char>); +} + +void UnrefSkTypeface(void* data) { + SkTypeface* skia_face = reinterpret_cast<SkTypeface*>(data); + SkSafeUnref(skia_face); +} + +// Creates a HarfBuzz face from the given Skia face. +hb_face_t* CreateHarfBuzzFace(SkTypeface* skia_face) { + SkSafeRef(skia_face); + hb_face_t* face = hb_face_create_for_tables(GetFontTable, skia_face, + UnrefSkTypeface); + DCHECK(face); + return face; +} + +// Creates a HarfBuzz font from the given Skia face and text size. +hb_font_t* CreateHarfBuzzFont(SkTypeface* skia_face, int text_size) { + typedef std::pair<hb_face_t*, GlyphCache> FaceCache; + + // TODO(ckocagil): This shouldn't grow indefinitely. Maybe use base::MRUCache? + static std::map<SkFontID, FaceCache> face_caches; + + FaceCache* face_cache = &face_caches[skia_face->uniqueID()]; + if (face_cache->first == 0) { + // These HarfBuzz faces live indefinitely and are intentionally leaked. + ANNOTATE_SCOPED_MEMORY_LEAK; + hb_face_t* harfbuzz_face = CreateHarfBuzzFace(skia_face); + *face_cache = FaceCache(harfbuzz_face, GlyphCache()); + } + + hb_font_t* harfbuzz_font = hb_font_create(face_cache->first); + // TODO(ckocagil): Investigate whether disabling hinting here has any effect + // on text quality. + int upem = hb_face_get_upem(face_cache->first); + hb_font_set_scale(harfbuzz_font, upem, upem); + FontData* hb_font_data = new FontData(&face_cache->second); + hb_font_data->paint_.setTypeface(skia_face); + hb_font_data->paint_.setTextSize(text_size); + hb_font_set_funcs(harfbuzz_font, g_font_funcs.Get().get(), hb_font_data, + DeleteByType<FontData>); + hb_font_make_immutable(harfbuzz_font); + return harfbuzz_font; +} + +// Returns true if characters of |block_code| may trigger font fallback. +bool IsUnusualBlockCode(UBlockCode block_code) { + return block_code == UBLOCK_GEOMETRIC_SHAPES || + block_code == UBLOCK_MISCELLANEOUS_SYMBOLS; +} + +// Returns the index of the first unusual character after a usual character or +// vice versa. Unusual characters are defined by |IsUnusualBlockCode|. +size_t FindUnusualCharacter(const base::string16& text, + size_t run_start, + size_t run_break) { + const int32 run_length = static_cast<int32>(run_break - run_start); + base::i18n::UTF16CharIterator iter(text.c_str() + run_start, + run_length); + const UBlockCode first_block_code = ublock_getCode(iter.get()); + const bool first_block_unusual = IsUnusualBlockCode(first_block_code); + while (iter.Advance() && iter.array_pos() < run_length) { + const UBlockCode current_block_code = ublock_getCode(iter.get()); + if (current_block_code != first_block_code && + (first_block_unusual || IsUnusualBlockCode(current_block_code))) { + return run_start + iter.array_pos(); + } + } + return run_break; +} + +// If the given scripts match, returns the one that isn't USCRIPT_COMMON or +// USCRIPT_INHERITED, i.e. the more specific one. Otherwise returns +// USCRIPT_INVALID_CODE. +UScriptCode ScriptIntersect(UScriptCode first, UScriptCode second) { + if (first == second || + (second > USCRIPT_INVALID_CODE && second <= USCRIPT_INHERITED)) { + return first; + } + if (first > USCRIPT_INVALID_CODE && first <= USCRIPT_INHERITED) + return second; + return USCRIPT_INVALID_CODE; +} + +// Writes the script and the script extensions of the character with the +// Unicode |codepoint|. Returns the number of written scripts. +int GetScriptExtensions(UChar32 codepoint, UScriptCode* scripts) { + UErrorCode icu_error = U_ZERO_ERROR; + // ICU documentation incorrectly states that the result of + // |uscript_getScriptExtensions| will contain the regular script property. + // Write the character's script property to the first element. + scripts[0] = uscript_getScript(codepoint, &icu_error); + if (U_FAILURE(icu_error)) + return 0; + // Fill the rest of |scripts| with the extensions. + int count = uscript_getScriptExtensions(codepoint, scripts + 1, + kMaxScripts - 1, &icu_error); + if (U_FAILURE(icu_error)) + count = 0; + return count + 1; +} + +// Intersects the script extensions set of |codepoint| with |result| and writes +// to |result|, reading and updating |result_size|. +void ScriptSetIntersect(UChar32 codepoint, + UScriptCode* result, + size_t* result_size) { + UScriptCode scripts[kMaxScripts] = { USCRIPT_INVALID_CODE }; + int count = GetScriptExtensions(codepoint, scripts); + + size_t out_size = 0; + + for (size_t i = 0; i < *result_size; ++i) { + for (int j = 0; j < count; ++j) { + UScriptCode intersection = ScriptIntersect(result[i], scripts[j]); + if (intersection != USCRIPT_INVALID_CODE) { + result[out_size++] = intersection; + break; + } + } + } + + *result_size = out_size; +} + +// Find the longest sequence of characters from 0 and up to |length| that +// have at least one common UScriptCode value. Writes the common script value to +// |script| and returns the length of the sequence. Takes the characters' script +// extensions into account. http://www.unicode.org/reports/tr24/#ScriptX +// +// Consider 3 characters with the script values {Kana}, {Hira, Kana}, {Kana}. +// Without script extensions only the first script in each set would be taken +// into account, resulting in 3 runs where 1 would be enough. +// TODO(ckocagil): Write a unit test for the case above. +int ScriptInterval(const base::string16& text, + size_t start, + size_t length, + UScriptCode* script) { + DCHECK_GT(length, 0U); + + UScriptCode scripts[kMaxScripts] = { USCRIPT_INVALID_CODE }; + + base::i18n::UTF16CharIterator char_iterator(text.c_str() + start, length); + size_t scripts_size = GetScriptExtensions(char_iterator.get(), scripts); + *script = scripts[0]; + + while (char_iterator.Advance()) { + ScriptSetIntersect(char_iterator.get(), scripts, &scripts_size); + if (scripts_size == 0U) + return char_iterator.array_pos(); + *script = scripts[0]; + } + + return length; +} + +// A port of hb_icu_script_to_script because harfbuzz on CrOS is built without +// hb-icu. See http://crbug.com/356929 +inline hb_script_t ICUScriptToHBScript(UScriptCode script) { + if (script == USCRIPT_INVALID_CODE) + return HB_SCRIPT_INVALID; + return hb_script_from_string(uscript_getShortName(script), -1); +} + +} // namespace + +namespace internal { + +TextRunHarfBuzz::TextRunHarfBuzz() + : width(0), + preceding_run_widths(0), + is_rtl(false), + level(0), + script(USCRIPT_INVALID_CODE), + glyph_count(-1), + font_size(0), + font_style(0), + strike(false), + diagonal_strike(false), + underline(false) {} + +TextRunHarfBuzz::~TextRunHarfBuzz() {} + +size_t TextRunHarfBuzz::CharToGlyph(size_t pos) const { + DCHECK(range.start() <= pos && pos < range.end()); + + if (!is_rtl) { + size_t cluster_start = 0; + for (size_t i = 1; i < glyph_count && pos >= glyph_to_char[i]; ++i) + if (glyph_to_char[i] != glyph_to_char[i - 1]) + cluster_start = i; + return cluster_start; + } + + for (size_t i = 0; i < glyph_count; ++i) { + if (pos >= glyph_to_char[i]) + return i; + } + NOTREACHED(); + return 0; +} + +Range TextRunHarfBuzz::CharRangeToGlyphRange(const Range& char_range) const { + DCHECK(range.Contains(char_range)); + DCHECK(!char_range.is_reversed()); + DCHECK(!char_range.is_empty()); + + size_t first = 0; + size_t last = 0; + + if (is_rtl) { + // For RTL runs, we subtract 1 from |char_range| to get the leading edges. + last = CharToGlyph(char_range.end() - 1); + // Loop until we find a non-empty glyph range. For multi-character clusters, + // the loop is needed to find the cluster end. Do the same for LTR below. + for (size_t i = char_range.start(); i > range.start(); --i) { + first = CharToGlyph(i - 1); + if (first != last) + return Range(last, first); + } + return Range(last, glyph_count); + } + + first = CharToGlyph(char_range.start()); + for (size_t i = char_range.end(); i < range.end(); ++i) { + last = CharToGlyph(i); + if (first != last) + return Range(first, last); + } + return Range(first, glyph_count); +} + +// Returns whether the given shaped run contains any missing glyphs. +bool TextRunHarfBuzz::HasMissingGlyphs() const { + static const int kMissingGlyphId = 0; + for (size_t i = 0; i < glyph_count; ++i) { + if (glyphs[i] == kMissingGlyphId) + return true; + } + return false; +} + +int TextRunHarfBuzz::GetGlyphXBoundary(size_t text_index, bool trailing) const { + if (text_index == range.end()) { + trailing = true; + --text_index; + } + Range glyph_range = CharRangeToGlyphRange(Range(text_index, text_index + 1)); + const size_t glyph_pos = (is_rtl == trailing) ? + glyph_range.start() : glyph_range.end(); + const int x = glyph_pos < glyph_count ? + SkScalarRoundToInt(positions[glyph_pos].x()) : width; + return preceding_run_widths + x; +} + +} // namespace internal + +RenderTextHarfBuzz::RenderTextHarfBuzz() + : RenderText(), + needs_layout_(false) {} + +RenderTextHarfBuzz::~RenderTextHarfBuzz() {} + +Size RenderTextHarfBuzz::GetStringSize() { + EnsureLayout(); + return lines()[0].size; +} + +SelectionModel RenderTextHarfBuzz::FindCursorPosition(const Point& point) { + EnsureLayout(); + + int x = ToTextPoint(point).x(); + int offset = 0; + size_t run_index = GetRunContainingXCoord(x, &offset); + if (run_index >= runs_.size()) + return EdgeSelectionModel((x < 0) ? CURSOR_LEFT : CURSOR_RIGHT); + const internal::TextRunHarfBuzz& run = *runs_[run_index]; + + for (size_t i = 0; i < run.glyph_count; ++i) { + const SkScalar end = + i + 1 == run.glyph_count ? run.width : run.positions[i + 1].x(); + const SkScalar middle = (end + run.positions[i].x()) / 2; + + if (offset < middle) { + return SelectionModel(LayoutIndexToTextIndex( + run.glyph_to_char[i] + (run.is_rtl ? 1 : 0)), + (run.is_rtl ? CURSOR_BACKWARD : CURSOR_FORWARD)); + } + if (offset < end) { + return SelectionModel(LayoutIndexToTextIndex( + run.glyph_to_char[i] + (run.is_rtl ? 0 : 1)), + (run.is_rtl ? CURSOR_FORWARD : CURSOR_BACKWARD)); + } + } + return EdgeSelectionModel(CURSOR_RIGHT); +} + +std::vector<RenderText::FontSpan> RenderTextHarfBuzz::GetFontSpansForTesting() { + NOTIMPLEMENTED(); + return std::vector<RenderText::FontSpan>(); +} + +int RenderTextHarfBuzz::GetLayoutTextBaseline() { + EnsureLayout(); + return lines()[0].baseline; +} + +SelectionModel RenderTextHarfBuzz::AdjacentCharSelectionModel( + const SelectionModel& selection, + VisualCursorDirection direction) { + DCHECK(!needs_layout_); + internal::TextRunHarfBuzz* run; + size_t run_index = GetRunContainingCaret(selection); + if (run_index >= runs_.size()) { + // The cursor is not in any run: we're at the visual and logical edge. + SelectionModel edge = EdgeSelectionModel(direction); + if (edge.caret_pos() == selection.caret_pos()) + return edge; + int visual_index = (direction == CURSOR_RIGHT) ? 0 : runs_.size() - 1; + run = runs_[visual_to_logical_[visual_index]]; + } else { + // If the cursor is moving within the current run, just move it by one + // grapheme in the appropriate direction. + run = runs_[run_index]; + size_t caret = selection.caret_pos(); + bool forward_motion = run->is_rtl == (direction == CURSOR_LEFT); + if (forward_motion) { + if (caret < LayoutIndexToTextIndex(run->range.end())) { + caret = IndexOfAdjacentGrapheme(caret, CURSOR_FORWARD); + return SelectionModel(caret, CURSOR_BACKWARD); + } + } else { + if (caret > LayoutIndexToTextIndex(run->range.start())) { + caret = IndexOfAdjacentGrapheme(caret, CURSOR_BACKWARD); + return SelectionModel(caret, CURSOR_FORWARD); + } + } + // The cursor is at the edge of a run; move to the visually adjacent run. + int visual_index = logical_to_visual_[run_index]; + visual_index += (direction == CURSOR_LEFT) ? -1 : 1; + if (visual_index < 0 || visual_index >= static_cast<int>(runs_.size())) + return EdgeSelectionModel(direction); + run = runs_[visual_to_logical_[visual_index]]; + } + bool forward_motion = run->is_rtl == (direction == CURSOR_LEFT); + return forward_motion ? FirstSelectionModelInsideRun(run) : + LastSelectionModelInsideRun(run); +} + +SelectionModel RenderTextHarfBuzz::AdjacentWordSelectionModel( + const SelectionModel& selection, + VisualCursorDirection direction) { + // TODO(ckocagil): This implementation currently matches RenderTextWin, but it + // should match the native behavior on other platforms. + if (obscured()) + return EdgeSelectionModel(direction); + + base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD); + bool success = iter.Init(); + DCHECK(success); + if (!success) + return selection; + + size_t pos; + if (direction == CURSOR_RIGHT) { + pos = std::min(selection.caret_pos() + 1, text().length()); + while (iter.Advance()) { + pos = iter.pos(); + if (iter.IsWord() && pos > selection.caret_pos()) + break; + } + } else { // direction == CURSOR_LEFT + // Notes: We always iterate words from the beginning. + // This is probably fast enough for our usage, but we may + // want to modify WordIterator so that it can start from the + // middle of string and advance backwards. + pos = std::max<int>(selection.caret_pos() - 1, 0); + while (iter.Advance()) { + if (iter.IsWord()) { + size_t begin = iter.pos() - iter.GetString().length(); + if (begin == selection.caret_pos()) { + // The cursor is at the beginning of a word. + // Move to previous word. + break; + } else if (iter.pos() >= selection.caret_pos()) { + // The cursor is in the middle or at the end of a word. + // Move to the top of current word. + pos = begin; + break; + } + pos = iter.pos() - iter.GetString().length(); + } + } + } + return SelectionModel(pos, CURSOR_FORWARD); +} + +Range RenderTextHarfBuzz::GetGlyphBounds(size_t index) { + const size_t run_index = + GetRunContainingCaret(SelectionModel(index, CURSOR_FORWARD)); + // Return edge bounds if the index is invalid or beyond the layout text size. + if (run_index >= runs_.size()) + return Range(GetStringSize().width()); + const size_t layout_index = TextIndexToLayoutIndex(index); + return Range(runs_[run_index]->GetGlyphXBoundary(layout_index, false), + runs_[run_index]->GetGlyphXBoundary(layout_index, true)); +} + +std::vector<Rect> RenderTextHarfBuzz::GetSubstringBounds(const Range& range) { + DCHECK(!needs_layout_); + DCHECK(Range(0, text().length()).Contains(range)); + Range layout_range(TextIndexToLayoutIndex(range.start()), + TextIndexToLayoutIndex(range.end())); + DCHECK(Range(0, GetLayoutText().length()).Contains(layout_range)); + + std::vector<Rect> rects; + if (layout_range.is_empty()) + return rects; + std::vector<Range> bounds; + + // Add a Range for each run/selection intersection. + // TODO(msw): The bounds should probably not always be leading the range ends. + for (size_t i = 0; i < runs_.size(); ++i) { + const internal::TextRunHarfBuzz* run = runs_[visual_to_logical_[i]]; + Range intersection = run->range.Intersect(layout_range); + if (intersection.IsValid()) { + DCHECK(!intersection.is_reversed()); + Range range_x(run->GetGlyphXBoundary(intersection.start(), false), + run->GetGlyphXBoundary(intersection.end(), false)); + if (range_x.is_empty()) + continue; + range_x = Range(range_x.GetMin(), range_x.GetMax()); + // Union this with the last range if they're adjacent. + DCHECK(bounds.empty() || bounds.back().GetMax() <= range_x.GetMin()); + if (!bounds.empty() && bounds.back().GetMax() == range_x.GetMin()) { + range_x = Range(bounds.back().GetMin(), range_x.GetMax()); + bounds.pop_back(); + } + bounds.push_back(range_x); + } + } + for (size_t i = 0; i < bounds.size(); ++i) { + std::vector<Rect> current_rects = TextBoundsToViewBounds(bounds[i]); + rects.insert(rects.end(), current_rects.begin(), current_rects.end()); + } + return rects; +} + +size_t RenderTextHarfBuzz::TextIndexToLayoutIndex(size_t index) const { + DCHECK_LE(index, text().length()); + ptrdiff_t i = obscured() ? UTF16IndexToOffset(text(), 0, index) : index; + CHECK_GE(i, 0); + // Clamp layout indices to the length of the text actually used for layout. + return std::min<size_t>(GetLayoutText().length(), i); +} + +size_t RenderTextHarfBuzz::LayoutIndexToTextIndex(size_t index) const { + if (!obscured()) + return index; + + DCHECK_LE(index, GetLayoutText().length()); + const size_t text_index = UTF16OffsetToIndex(text(), 0, index); + DCHECK_LE(text_index, text().length()); + return text_index; +} + +bool RenderTextHarfBuzz::IsValidCursorIndex(size_t index) { + if (index == 0 || index == text().length()) + return true; + if (!IsValidLogicalIndex(index)) + return false; + EnsureLayout(); + // Disallow indices amid multi-character graphemes by checking glyph bounds. + // These characters are not surrogate-pairs, but may yield a single glyph: + // \x0915\x093f - (ki) - one of many Devanagari biconsonantal conjuncts. + // \x0e08\x0e33 - (cho chan + sara am) - a Thai consonant and vowel pair. + return GetGlyphBounds(index) != GetGlyphBounds(index - 1); +} + +void RenderTextHarfBuzz::ResetLayout() { + needs_layout_ = true; +} + +void RenderTextHarfBuzz::EnsureLayout() { + if (needs_layout_) { + runs_.clear(); + + if (!GetLayoutText().empty()) { + ItemizeText(); + + for (size_t i = 0; i < runs_.size(); ++i) + ShapeRun(runs_[i]); + + // Precalculate run width information. + size_t preceding_run_widths = 0; + for (size_t i = 0; i < runs_.size(); ++i) { + internal::TextRunHarfBuzz* run = runs_[visual_to_logical_[i]]; + run->preceding_run_widths = preceding_run_widths; + preceding_run_widths += run->width; + } + } + + needs_layout_ = false; + std::vector<internal::Line> empty_lines; + set_lines(&empty_lines); + } + + if (lines().empty()) { + std::vector<internal::Line> lines; + lines.push_back(internal::Line()); + lines[0].baseline = font_list().GetBaseline(); + lines[0].size.set_height(font_list().GetHeight()); + + int current_x = 0; + SkPaint paint; + + for (size_t i = 0; i < runs_.size(); ++i) { + const internal::TextRunHarfBuzz& run = *runs_[visual_to_logical_[i]]; + internal::LineSegment segment; + segment.x_range = Range(current_x, current_x + run.width); + segment.char_range = run.range; + segment.run = i; + lines[0].segments.push_back(segment); + + paint.setTypeface(run.skia_face.get()); + paint.setTextSize(run.font_size); + SkPaint::FontMetrics metrics; + paint.getFontMetrics(&metrics); + + lines[0].size.set_width(lines[0].size.width() + run.width); + lines[0].size.set_height(std::max(lines[0].size.height(), + SkScalarRoundToInt(metrics.fDescent - metrics.fAscent))); + lines[0].baseline = std::max(lines[0].baseline, + SkScalarRoundToInt(-metrics.fAscent)); + } + + set_lines(&lines); + } +} + +void RenderTextHarfBuzz::DrawVisualText(Canvas* canvas) { + DCHECK(!needs_layout_); + + int current_x = 0; + + internal::SkiaTextRenderer renderer(canvas); + ApplyFadeEffects(&renderer); + ApplyTextShadows(&renderer); + +#if defined(OS_WIN) + bool smoothing_enabled; + bool cleartype_enabled; + GetCachedFontSmoothingSettings(&smoothing_enabled, &cleartype_enabled); + // Note that |cleartype_enabled| corresponds to Skia's |enable_lcd_text|. + renderer.SetFontSmoothingSettings( + smoothing_enabled, cleartype_enabled && !background_is_transparent(), + smoothing_enabled /* subpixel_positioning */); +#endif + + ApplyCompositionAndSelectionStyles(); + + const Vector2d line_offset = GetLineOffset(0); + + for (size_t i = 0; i < runs_.size(); ++i) { + const internal::TextRunHarfBuzz& run = *runs_[visual_to_logical_[i]]; + renderer.SetTypeface(run.skia_face.get()); + renderer.SetTextSize(run.font_size); + + canvas->Save(); + Vector2d origin = line_offset + Vector2d(current_x, lines()[0].baseline); + canvas->Translate(origin); + + for (BreakList<SkColor>::const_iterator it = + colors().GetBreak(run.range.start()); + it != colors().breaks().end() && it->first < run.range.end(); + ++it) { + const Range intersection = colors().GetRange(it).Intersect(run.range); + const Range colored_glyphs = run.CharRangeToGlyphRange(intersection); + // The range may be empty if a portion of a multi-character grapheme is + // selected, yielding two colors for a single glyph. For now, this just + // paints the glyph with a single style, but it should paint it twice, + // clipped according to selection bounds. See http://crbug.com/366786 + if (colored_glyphs.is_empty()) + continue; + + renderer.SetForegroundColor(it->second); + renderer.DrawPosText(&run.positions[colored_glyphs.start()], + &run.glyphs[colored_glyphs.start()], + colored_glyphs.length()); + int width = (colored_glyphs.end() == run.glyph_count ? run.width : + run.positions[colored_glyphs.end()].x()) - + run.positions[colored_glyphs.start()].x(); + renderer.DrawDecorations(0, 0, width, run.underline, run.strike, + run.diagonal_strike); + } + + canvas->Restore(); + current_x += run.width; + } + + renderer.EndDiagonalStrike(); + + UndoCompositionAndSelectionStyles(); +} + +size_t RenderTextHarfBuzz::GetRunContainingCaret( + const SelectionModel& caret) const { + DCHECK(!needs_layout_); + size_t layout_position = TextIndexToLayoutIndex(caret.caret_pos()); + LogicalCursorDirection affinity = caret.caret_affinity(); + for (size_t run = 0; run < runs_.size(); ++run) { + if (RangeContainsCaret(runs_[run]->range, layout_position, affinity)) + return run; + } + return runs_.size(); +} + +size_t RenderTextHarfBuzz::GetRunContainingXCoord(int x, int* offset) const { + DCHECK(!needs_layout_); + if (x < 0) + return runs_.size(); + // Find the text run containing the argument point (assumed already offset). + int current_x = 0; + for (size_t i = 0; i < runs_.size(); ++i) { + size_t run = visual_to_logical_[i]; + current_x += runs_[run]->width; + if (x < current_x) { + *offset = x - (current_x - runs_[run]->width); + return run; + } + } + return runs_.size(); +} + +SelectionModel RenderTextHarfBuzz::FirstSelectionModelInsideRun( + const internal::TextRunHarfBuzz* run) { + size_t position = LayoutIndexToTextIndex(run->range.start()); + position = IndexOfAdjacentGrapheme(position, CURSOR_FORWARD); + return SelectionModel(position, CURSOR_BACKWARD); +} + +SelectionModel RenderTextHarfBuzz::LastSelectionModelInsideRun( + const internal::TextRunHarfBuzz* run) { + size_t position = LayoutIndexToTextIndex(run->range.end()); + position = IndexOfAdjacentGrapheme(position, CURSOR_BACKWARD); + return SelectionModel(position, CURSOR_FORWARD); +} + +void RenderTextHarfBuzz::ItemizeText() { + const base::string16& text = GetLayoutText(); + const bool is_text_rtl = GetTextDirection() == base::i18n::RIGHT_TO_LEFT; + DCHECK_NE(0U, text.length()); + + // If ICU fails to itemize the text, we create a run that spans the entire + // text. This is needed because leaving the runs set empty causes some clients + // to misbehave since they expect non-zero text metrics from a non-empty text. + base::i18n::BiDiLineIterator bidi_iterator; + if (!bidi_iterator.Open(text, is_text_rtl, false)) { + internal::TextRunHarfBuzz* run = new internal::TextRunHarfBuzz; + run->range = Range(0, text.length()); + runs_.push_back(run); + visual_to_logical_ = logical_to_visual_ = std::vector<int32_t>(1, 0); + return; + } + + // Temporarily apply composition underlines and selection colors. + ApplyCompositionAndSelectionStyles(); + + // Build the list of runs from the script items and ranged styles. Use an + // empty color BreakList to avoid breaking runs at color boundaries. + BreakList<SkColor> empty_colors; + empty_colors.SetMax(text.length()); + internal::StyleIterator style(empty_colors, styles()); + + for (size_t run_break = 0; run_break < text.length();) { + internal::TextRunHarfBuzz* run = new internal::TextRunHarfBuzz; + run->range.set_start(run_break); + run->font_style = (style.style(BOLD) ? Font::BOLD : 0) | + (style.style(ITALIC) ? Font::ITALIC : 0); + run->strike = style.style(STRIKE); + run->diagonal_strike = style.style(DIAGONAL_STRIKE); + run->underline = style.style(UNDERLINE); + + int32 script_item_break = 0; + bidi_iterator.GetLogicalRun(run_break, &script_item_break, &run->level); + // Odd BiDi embedding levels correspond to RTL runs. + run->is_rtl = (run->level % 2) == 1; + // Find the length and script of this script run. + script_item_break = ScriptInterval(text, run_break, + script_item_break - run_break, &run->script) + run_break; + + // Find the next break and advance the iterators as needed. + run_break = std::min(static_cast<size_t>(script_item_break), + TextIndexToLayoutIndex(style.GetRange().end())); + + // Break runs adjacent to character substrings in certain code blocks. + // This avoids using their fallback fonts for more characters than needed, + // in cases like "\x25B6 Media Title", etc. http://crbug.com/278913 + if (run_break > run->range.start()) + run_break = FindUnusualCharacter(text, run->range.start(), run_break); + + DCHECK(IsValidCodePointIndex(text, run_break)); + style.UpdatePosition(LayoutIndexToTextIndex(run_break)); + run->range.set_end(run_break); + + runs_.push_back(run); + } + + // Undo the temporarily applied composition underlines and selection colors. + UndoCompositionAndSelectionStyles(); + + const size_t num_runs = runs_.size(); + std::vector<UBiDiLevel> levels(num_runs); + for (size_t i = 0; i < num_runs; ++i) + levels[i] = runs_[i]->level; + visual_to_logical_.resize(num_runs); + ubidi_reorderVisual(&levels[0], num_runs, &visual_to_logical_[0]); + logical_to_visual_.resize(num_runs); + ubidi_reorderLogical(&levels[0], num_runs, &logical_to_visual_[0]); +} + +void RenderTextHarfBuzz::ShapeRun(internal::TextRunHarfBuzz* run) { + const base::string16& text = GetLayoutText(); + // TODO(ckocagil|yukishiino): Implement font fallback. + const Font& primary_font = font_list().GetPrimaryFont(); + run->skia_face = internal::CreateSkiaTypeface(primary_font.GetFontName(), + run->font_style); + run->font_size = primary_font.GetFontSize(); + + hb_font_t* harfbuzz_font = CreateHarfBuzzFont(run->skia_face.get(), + run->font_size); + + // Create a HarfBuzz buffer and add the string to be shaped. The HarfBuzz + // buffer holds our text, run information to be used by the shaping engine, + // and the resulting glyph data. + hb_buffer_t* buffer = hb_buffer_create(); + hb_buffer_add_utf16(buffer, reinterpret_cast<const uint16*>(text.c_str()), + text.length(), run->range.start(), run->range.length()); + hb_buffer_set_script(buffer, ICUScriptToHBScript(run->script)); + hb_buffer_set_direction(buffer, + run->is_rtl ? HB_DIRECTION_RTL : HB_DIRECTION_LTR); + // TODO(ckocagil): Should we determine the actual language? + hb_buffer_set_language(buffer, hb_language_get_default()); + + // Shape the text. + hb_shape(harfbuzz_font, buffer, NULL, 0); + + // Populate the run fields with the resulting glyph data in the buffer. + unsigned int glyph_count = 0; + hb_glyph_info_t* infos = hb_buffer_get_glyph_infos(buffer, &glyph_count); + hb_glyph_position_t* hb_positions = hb_buffer_get_glyph_positions(buffer, + NULL); + run->glyph_count = glyph_count; + run->glyphs.reset(new uint16[run->glyph_count]); + run->glyph_to_char.reset(new uint32[run->glyph_count]); + run->positions.reset(new SkPoint[run->glyph_count]); + for (size_t i = 0; i < run->glyph_count; ++i) { + run->glyphs[i] = infos[i].codepoint; + run->glyph_to_char[i] = infos[i].cluster; + const int x_offset = + SkScalarRoundToInt(SkFixedToScalar(hb_positions[i].x_offset)); + const int y_offset = + SkScalarRoundToInt(SkFixedToScalar(hb_positions[i].y_offset)); + run->positions[i].set(run->width + x_offset, y_offset); + run->width += + SkScalarRoundToInt(SkFixedToScalar(hb_positions[i].x_advance)); + } + + hb_buffer_destroy(buffer); + hb_font_destroy(harfbuzz_font); +} + +} // namespace gfx diff --git a/chromium/ui/gfx/render_text_harfbuzz.h b/chromium/ui/gfx/render_text_harfbuzz.h new file mode 100644 index 00000000000..59ce92f21c3 --- /dev/null +++ b/chromium/ui/gfx/render_text_harfbuzz.h @@ -0,0 +1,130 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_GFX_RENDER_TEXT_HARFBUZZ_H_ +#define UI_GFX_RENDER_TEXT_HARFBUZZ_H_ + +#include "base/memory/scoped_ptr.h" +#include "base/memory/scoped_vector.h" +#include "third_party/harfbuzz-ng/src/hb.h" +#include "third_party/icu/source/common/unicode/ubidi.h" +#include "third_party/icu/source/common/unicode/uscript.h" +#include "ui/gfx/render_text.h" + +namespace gfx { + +namespace internal { + +struct GFX_EXPORT TextRunHarfBuzz { + TextRunHarfBuzz(); + ~TextRunHarfBuzz(); + + // Returns the index of the first glyph that corresponds to the character at + // |pos|. + size_t CharToGlyph(size_t pos) const; + + // Returns the corresponding glyph range of the given character range. + // |range| is in text-space (0 corresponds to |GetLayoutText()[0]|). Returned + // value is in run-space (0 corresponds to the first glyph in the run). + Range CharRangeToGlyphRange(const Range& range) const; + + // Returns whether the given shaped run contains any missing glyphs. + bool HasMissingGlyphs() const; + + // Returns the X coordinate of the leading or |trailing| edge of the glyph + // starting at |text_index|, relative to the left of the text (not the view). + int GetGlyphXBoundary(size_t text_index, bool trailing) const; + + int width; + int preceding_run_widths; + Range range; + bool is_rtl; + UBiDiLevel level; + UScriptCode script; + + scoped_ptr<uint16[]> glyphs; + scoped_ptr<SkPoint[]> positions; + scoped_ptr<uint32[]> glyph_to_char; + size_t glyph_count; + + skia::RefPtr<SkTypeface> skia_face; + int font_size; + int font_style; + bool strike; + bool diagonal_strike; + bool underline; + + private: + DISALLOW_COPY_AND_ASSIGN(TextRunHarfBuzz); +}; + +} // namespace internal + +class GFX_EXPORT RenderTextHarfBuzz : public RenderText { + public: + RenderTextHarfBuzz(); + virtual ~RenderTextHarfBuzz(); + + // Overridden from RenderText. + virtual Size GetStringSize() OVERRIDE; + virtual SelectionModel FindCursorPosition(const Point& point) OVERRIDE; + virtual std::vector<FontSpan> GetFontSpansForTesting() OVERRIDE; + + protected: + // Overridden from RenderText. + virtual int GetLayoutTextBaseline() OVERRIDE; + virtual SelectionModel AdjacentCharSelectionModel( + const SelectionModel& selection, + VisualCursorDirection direction) OVERRIDE; + virtual SelectionModel AdjacentWordSelectionModel( + const SelectionModel& selection, + VisualCursorDirection direction) OVERRIDE; + virtual Range GetGlyphBounds(size_t index) OVERRIDE; + virtual std::vector<Rect> GetSubstringBounds(const Range& range) OVERRIDE; + virtual size_t TextIndexToLayoutIndex(size_t index) const OVERRIDE; + virtual size_t LayoutIndexToTextIndex(size_t index) const OVERRIDE; + virtual bool IsValidCursorIndex(size_t index) OVERRIDE; + virtual void ResetLayout() OVERRIDE; + virtual void EnsureLayout() OVERRIDE; + virtual void DrawVisualText(Canvas* canvas) OVERRIDE; + + private: + friend class RenderTextTest; + FRIEND_TEST_ALL_PREFIXES(RenderTextTest, HarfBuzz_RunDirection); + FRIEND_TEST_ALL_PREFIXES(RenderTextTest, HarfBuzz_BreakRunsByUnicodeBlocks); + + // Return the run index that contains the argument; or the length of the + // |runs_| vector if argument exceeds the text length or width. + size_t GetRunContainingCaret(const SelectionModel& caret) const; + size_t GetRunContainingXCoord(int x, int* offset) const; + + // Given a |run|, returns the SelectionModel that contains the logical first + // or last caret position inside (not at a boundary of) the run. + // The returned value represents a cursor/caret position without a selection. + SelectionModel FirstSelectionModelInsideRun( + const internal::TextRunHarfBuzz* run); + SelectionModel LastSelectionModelInsideRun( + const internal::TextRunHarfBuzz* run); + + // Break the text into logical runs and populate the visual <-> logical maps. + void ItemizeText(); + + // Shape the glyphs needed for the text |run|. + void ShapeRun(internal::TextRunHarfBuzz* run); + + // Text runs in logical order. + ScopedVector<internal::TextRunHarfBuzz> runs_; + + // Maps visual run indices to logical run indices and vice versa. + std::vector<int32_t> visual_to_logical_; + std::vector<int32_t> logical_to_visual_; + + bool needs_layout_; + + DISALLOW_COPY_AND_ASSIGN(RenderTextHarfBuzz); +}; + +} // namespace gfx + +#endif // UI_GFX_RENDER_TEXT_HARFBUZZ_H_ diff --git a/chromium/ui/gfx/render_text_mac.cc b/chromium/ui/gfx/render_text_mac.cc index 4feb9302c18..68780f84b94 100644 --- a/chromium/ui/gfx/render_text_mac.cc +++ b/chromium/ui/gfx/render_text_mac.cc @@ -45,7 +45,7 @@ std::vector<RenderText::FontSpan> RenderTextMac::GetFontSpansForTesting() { std::vector<RenderText::FontSpan> spans; for (size_t i = 0; i < runs_.size(); ++i) { - gfx::Font font(runs_[i].font_name, runs_[i].text_size); + Font font(runs_[i].font_name, runs_[i].text_size); const CFRange cf_range = CTRunGetStringRange(runs_[i].ct_run); const Range range(cf_range.location, cf_range.location + cf_range.length); spans.push_back(RenderText::FontSpan(font, range)); @@ -93,9 +93,9 @@ size_t RenderTextMac::LayoutIndexToTextIndex(size_t index) const { return index; } -bool RenderTextMac::IsCursorablePosition(size_t position) { +bool RenderTextMac::IsValidCursorIndex(size_t index) { // TODO(asvitkine): Implement this. http://crbug.com/131618 - return true; + return IsValidLogicalIndex(index); } void RenderTextMac::ResetLayout() { @@ -111,8 +111,8 @@ void RenderTextMac::EnsureLayout() { runs_.clear(); runs_valid_ = false; - const Font& font = GetPrimaryFont(); - CTFontRef ct_font = base::mac::NSToCFCast(font.GetNativeFont()); + CTFontRef ct_font = base::mac::NSToCFCast( + font_list().GetPrimaryFont().GetNativeFont()); const void* keys[] = { kCTFontAttributeName }; const void* values[] = { ct_font }; @@ -174,6 +174,8 @@ void RenderTextMac::DrawVisualText(Canvas* canvas) { renderer.DrawDecorations(run.origin.x(), run.origin.y(), run.width, run.underline, run.strike, run.diagonal_strike); } + + renderer.EndDiagonalStrike(); } RenderTextMac::TextRun::TextRun() @@ -209,7 +211,7 @@ void RenderTextMac::ApplyStyles(CFMutableAttributedStringRef attr_string, end = TextIndexToLayoutIndex(style.GetRange().end()); const CFRange range = CFRangeMake(i, end - i); base::ScopedCFTypeRef<CGColorRef> foreground( - gfx::CGColorCreateFromSkColor(style.color())); + CGColorCreateFromSkColor(style.color())); CFAttributedStringSetAttribute(attr_string, range, kCTForegroundColorAttributeName, foreground); CFArrayAppendValue(attributes_, foreground); @@ -252,9 +254,9 @@ void RenderTextMac::ComputeRuns() { // TODO(asvitkine): Don't use GetLineOffset() until draw time, since it may be // updated based on alignment changes without resetting the layout. - gfx::Vector2d text_offset = GetLineOffset(0); + Vector2d text_offset = GetLineOffset(0); // Skia will draw glyphs with respect to the baseline. - text_offset += gfx::Vector2d(0, common_baseline_); + text_offset += Vector2d(0, common_baseline_); const SkScalar x = SkIntToScalar(text_offset.x()); const SkScalar y = SkIntToScalar(text_offset.y()); @@ -323,7 +325,7 @@ void RenderTextMac::ComputeRuns() { base::mac::GetValueFromDictionary<CGColorRef>( attributes, kCTForegroundColorAttributeName); if (foreground) - run->foreground = gfx::CGColorRefToSkColor(foreground); + run->foreground = CGColorRefToSkColor(foreground); const CFNumberRef underline = base::mac::GetValueFromDictionary<CFNumberRef>( @@ -337,7 +339,7 @@ void RenderTextMac::ComputeRuns() { runs_valid_ = true; } -RenderText* RenderText::CreateInstance() { +RenderText* RenderText::CreateNativeInstance() { return new RenderTextMac; } diff --git a/chromium/ui/gfx/render_text_mac.h b/chromium/ui/gfx/render_text_mac.h index 3a18a0fad45..ad03b845935 100644 --- a/chromium/ui/gfx/render_text_mac.h +++ b/chromium/ui/gfx/render_text_mac.h @@ -44,7 +44,7 @@ class RenderTextMac : public RenderText { virtual std::vector<Rect> GetSubstringBounds(const Range& range) OVERRIDE; virtual size_t TextIndexToLayoutIndex(size_t index) const OVERRIDE; virtual size_t LayoutIndexToTextIndex(size_t index) const OVERRIDE; - virtual bool IsCursorablePosition(size_t position) OVERRIDE; + virtual bool IsValidCursorIndex(size_t index) OVERRIDE; virtual void ResetLayout() OVERRIDE; virtual void EnsureLayout() OVERRIDE; virtual void DrawVisualText(Canvas* canvas) OVERRIDE; diff --git a/chromium/ui/gfx/render_text_ozone.cc b/chromium/ui/gfx/render_text_ozone.cc index 058b5a6389d..fb5ef993d8b 100644 --- a/chromium/ui/gfx/render_text_ozone.cc +++ b/chromium/ui/gfx/render_text_ozone.cc @@ -6,7 +6,7 @@ namespace gfx { -RenderText* RenderText::CreateInstance() { +RenderText* RenderText::CreateNativeInstance() { return NULL; } diff --git a/chromium/ui/gfx/render_text_pango.cc b/chromium/ui/gfx/render_text_pango.cc index f120a5c10bb..a1269264617 100644 --- a/chromium/ui/gfx/render_text_pango.cc +++ b/chromium/ui/gfx/render_text_pango.cc @@ -40,8 +40,7 @@ bool IsForwardMotion(VisualCursorDirection direction, const PangoItem* item) { } // Checks whether |range| contains |index|. This is not the same as calling -// |range.Contains(gfx::Range(index))| - as that would return true when -// |index| == |range.end()|. +// range.Contains(Range(index)), which returns true if |index| == |range.end()|. bool IndexInRange(const Range& range, size_t index) { return index >= range.start() && index < range.end(); } @@ -211,6 +210,7 @@ SelectionModel RenderTextPango::AdjacentWordSelectionModel( } Range RenderTextPango::GetGlyphBounds(size_t index) { + EnsureLayout(); PangoRectangle pos; pango_layout_index_to_pos(layout_, TextIndexToLayoutIndex(index), &pos); // TODO(derat): Support fractional ranges for subpixel positioning? @@ -248,7 +248,7 @@ std::vector<Rect> RenderTextPango::GetSubstringBounds(const Range& range) { size_t RenderTextPango::TextIndexToLayoutIndex(size_t index) const { DCHECK(layout_); - ptrdiff_t offset = gfx::UTF16IndexToOffset(text(), 0, index); + ptrdiff_t offset = UTF16IndexToOffset(text(), 0, index); // Clamp layout indices to the length of the text actually used for layout. offset = std::min<size_t>(offset, g_utf8_strlen(layout_text_, -1)); const char* layout_pointer = g_utf8_offset_to_pointer(layout_text_, offset); @@ -259,24 +259,19 @@ size_t RenderTextPango::LayoutIndexToTextIndex(size_t index) const { DCHECK(layout_); const char* layout_pointer = layout_text_ + index; const long offset = g_utf8_pointer_to_offset(layout_text_, layout_pointer); - return gfx::UTF16OffsetToIndex(text(), 0, offset); + return UTF16OffsetToIndex(text(), 0, offset); } -bool RenderTextPango::IsCursorablePosition(size_t position) { - if (position == 0 && text().empty()) +bool RenderTextPango::IsValidCursorIndex(size_t index) { + if (index == 0 || index == text().length()) return true; - if (position >= text().length()) - return position == text().length(); - if (!gfx::IsValidCodePointIndex(text(), position)) + if (!IsValidLogicalIndex(index)) return false; EnsureLayout(); - ptrdiff_t offset = gfx::UTF16IndexToOffset(text(), 0, position); - // Check that the index corresponds with a valid text code point, that it is - // marked as a legitimate cursor position by Pango, and that it is not - // truncated from layout text (its glyph is shown on screen). - return (offset < num_log_attrs_ && log_attrs_[offset].is_cursor_position && - offset < g_utf8_strlen(layout_text_, -1)); + ptrdiff_t offset = UTF16IndexToOffset(text(), 0, index); + // Check that the index is marked as a legitimate cursor position by Pango. + return offset < num_log_attrs_ && log_attrs_[offset].is_cursor_position; } void RenderTextPango::ResetLayout() { @@ -353,7 +348,7 @@ void RenderTextPango::SetupPangoAttributes(PangoLayout* layout) { const size_t italic_end = styles()[ITALIC].GetRange(italic).end(); const size_t style_end = std::min(bold_end, italic_end); if (style != font_list().GetFontStyle()) { - FontList derived_font_list = font_list().DeriveFontList(style); + FontList derived_font_list = font_list().DeriveWithStyle(style); ScopedPangoFontDescription desc(pango_font_description_from_string( derived_font_list.GetFontDescriptionString().c_str())); @@ -390,14 +385,31 @@ void RenderTextPango::DrawVisualText(Canvas* canvas) { ApplyTextShadows(&renderer); // TODO(derat): Use font-specific params: http://crbug.com/125235 - const gfx::FontRenderParams& render_params = - gfx::GetDefaultFontRenderParams(); + const FontRenderParams& render_params = GetDefaultFontRenderParams(); const bool use_subpixel_rendering = render_params.subpixel_rendering != - gfx::FontRenderParams::SUBPIXEL_RENDERING_NONE; + FontRenderParams::SUBPIXEL_RENDERING_NONE; renderer.SetFontSmoothingSettings( render_params.antialiasing, - use_subpixel_rendering && !background_is_transparent()); + use_subpixel_rendering && !background_is_transparent(), + render_params.subpixel_positioning); + + SkPaint::Hinting skia_hinting = SkPaint::kNormal_Hinting; + switch (render_params.hinting) { + case FontRenderParams::HINTING_NONE: + skia_hinting = SkPaint::kNo_Hinting; + break; + case FontRenderParams::HINTING_SLIGHT: + skia_hinting = SkPaint::kSlight_Hinting; + break; + case FontRenderParams::HINTING_MEDIUM: + skia_hinting = SkPaint::kNormal_Hinting; + break; + case FontRenderParams::HINTING_FULL: + skia_hinting = SkPaint::kFull_Hinting; + break; + } + renderer.SetFontHinting(skia_hinting); // Temporarily apply composition underlines and selection colors. ApplyCompositionAndSelectionStyles(); @@ -468,6 +480,8 @@ void RenderTextPango::DrawVisualText(Canvas* canvas) { } while (glyph_index < glyph_count); } + renderer.EndDiagonalStrike(); + // Undo the temporarily applied composition underlines and selection colors. UndoCompositionAndSelectionStyles(); } @@ -507,7 +521,7 @@ size_t RenderTextPango::GetGlyphTextIndex(PangoLayoutRun* run, run->glyphs->log_clusters[glyph_index]); } -RenderText* RenderText::CreateInstance() { +RenderText* RenderText::CreateNativeInstance() { return new RenderTextPango; } diff --git a/chromium/ui/gfx/render_text_pango.h b/chromium/ui/gfx/render_text_pango.h index ba7361c4d30..4c62e0aab89 100644 --- a/chromium/ui/gfx/render_text_pango.h +++ b/chromium/ui/gfx/render_text_pango.h @@ -36,7 +36,7 @@ class RenderTextPango : public RenderText { virtual std::vector<Rect> GetSubstringBounds(const Range& range) OVERRIDE; virtual size_t TextIndexToLayoutIndex(size_t index) const OVERRIDE; virtual size_t LayoutIndexToTextIndex(size_t index) const OVERRIDE; - virtual bool IsCursorablePosition(size_t position) OVERRIDE; + virtual bool IsValidCursorIndex(size_t index) OVERRIDE; virtual void ResetLayout() OVERRIDE; virtual void EnsureLayout() OVERRIDE; virtual void DrawVisualText(Canvas* canvas) OVERRIDE; diff --git a/chromium/ui/gfx/render_text_unittest.cc b/chromium/ui/gfx/render_text_unittest.cc index c8c50d42814..4686066f088 100644 --- a/chromium/ui/gfx/render_text_unittest.cc +++ b/chromium/ui/gfx/render_text_unittest.cc @@ -14,6 +14,7 @@ #include "testing/gtest/include/gtest/gtest.h" #include "ui/gfx/break_list.h" #include "ui/gfx/canvas.h" +#include "ui/gfx/render_text_harfbuzz.h" #if defined(OS_WIN) #include "base/win/windows_version.h" @@ -24,9 +25,9 @@ #include "ui/gfx/render_text_pango.h" #endif -#if defined(TOOLKIT_GTK) -#include <gtk/gtk.h> -#endif +using base::ASCIIToUTF16; +using base::UTF8ToUTF16; +using base::WideToUTF16; namespace gfx { @@ -44,8 +45,7 @@ const wchar_t kRtlLtrRtl[] = L"\x5d0" L"a" L"\x5d1"; #endif // Checks whether |range| contains |index|. This is not the same as calling -// |range.Contains(gfx::Range(index))| - as that would return true when -// |index| == |range.end()|. +// range.Contains(Range(index)), which returns true if |index| == |range.end()|. bool IndexInRange(const Range& range, size_t index) { return index >= range.start() && index < range.end(); } @@ -59,10 +59,6 @@ base::string16 GetSelectedText(RenderText* render_text) { void SetRTL(bool rtl) { // Override the current locale/direction. base::i18n::SetICUDefaultLocale(rtl ? "he" : "en"); -#if defined(TOOLKIT_GTK) - // Do the same for GTK, which does not rely on the ICU default locale. - gtk_widget_set_default_direction(rtl ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR); -#endif EXPECT_EQ(rtl, base::i18n::IsRTL()); } @@ -283,10 +279,10 @@ TEST_F(RenderTextTest, ObscuredText) { render_text->SetObscured(true); // Surrogate pairs are counted as one code point. - const char16 invalid_surrogates[] = {0xDC00, 0xD800, 0}; + const base::char16 invalid_surrogates[] = {0xDC00, 0xD800, 0}; render_text->SetText(invalid_surrogates); EXPECT_EQ(ASCIIToUTF16("**"), render_text->GetLayoutText()); - const char16 valid_surrogates[] = {0xD800, 0xDC00, 0}; + const base::char16 valid_surrogates[] = {0xD800, 0xDC00, 0}; render_text->SetText(valid_surrogates); EXPECT_EQ(ASCIIToUTF16("*"), render_text->GetLayoutText()); EXPECT_EQ(0U, render_text->cursor_position()); @@ -299,9 +295,9 @@ TEST_F(RenderTextTest, ObscuredText) { EXPECT_EQ(1U, render_text->TextIndexToLayoutIndex(2U)); EXPECT_EQ(0U, render_text->LayoutIndexToTextIndex(0U)); EXPECT_EQ(2U, render_text->LayoutIndexToTextIndex(1U)); - EXPECT_TRUE(render_text->IsCursorablePosition(0U)); - EXPECT_FALSE(render_text->IsCursorablePosition(1U)); - EXPECT_TRUE(render_text->IsCursorablePosition(2U)); + EXPECT_TRUE(render_text->IsValidCursorIndex(0U)); + EXPECT_FALSE(render_text->IsValidCursorIndex(1U)); + EXPECT_TRUE(render_text->IsValidCursorIndex(2U)); // FindCursorPosition() should not return positions between a surrogate pair. render_text->SetDisplayRect(Rect(0, 0, 20, 20)); @@ -370,37 +366,127 @@ TEST_F(RenderTextTest, RevealObscuredText) { EXPECT_EQ(ASCIIToUTF16("**********"), render_text->GetLayoutText()); // Text with invalid surrogates. - const char16 invalid_surrogates[] = {0xDC00, 0xD800, 'h', 'o', 'p', 0}; + const base::char16 invalid_surrogates[] = {0xDC00, 0xD800, 'h', 'o', 'p', 0}; render_text->SetText(invalid_surrogates); EXPECT_EQ(ASCIIToUTF16("*****"), render_text->GetLayoutText()); render_text->RenderText::SetObscuredRevealIndex(0); - const char16 invalid_expect_0[] = {0xDC00, '*', '*', '*', '*', 0}; + const base::char16 invalid_expect_0[] = {0xDC00, '*', '*', '*', '*', 0}; EXPECT_EQ(invalid_expect_0, render_text->GetLayoutText()); render_text->RenderText::SetObscuredRevealIndex(1); - const char16 invalid_expect_1[] = {'*', 0xD800, '*', '*', '*', 0}; + const base::char16 invalid_expect_1[] = {'*', 0xD800, '*', '*', '*', 0}; EXPECT_EQ(invalid_expect_1, render_text->GetLayoutText()); render_text->RenderText::SetObscuredRevealIndex(2); EXPECT_EQ(ASCIIToUTF16("**h**"), render_text->GetLayoutText()); // Text with valid surrogates before and after the reveal index. - const char16 valid_surrogates[] = + const base::char16 valid_surrogates[] = {0xD800, 0xDC00, 'h', 'o', 'p', 0xD800, 0xDC00, 0}; render_text->SetText(valid_surrogates); EXPECT_EQ(ASCIIToUTF16("*****"), render_text->GetLayoutText()); render_text->RenderText::SetObscuredRevealIndex(0); - const char16 valid_expect_0_and_1[] = {0xD800, 0xDC00, '*', '*', '*', '*', 0}; + const base::char16 valid_expect_0_and_1[] = + {0xD800, 0xDC00, '*', '*', '*', '*', 0}; EXPECT_EQ(valid_expect_0_and_1, render_text->GetLayoutText()); render_text->RenderText::SetObscuredRevealIndex(1); EXPECT_EQ(valid_expect_0_and_1, render_text->GetLayoutText()); render_text->RenderText::SetObscuredRevealIndex(2); EXPECT_EQ(ASCIIToUTF16("*h***"), render_text->GetLayoutText()); render_text->RenderText::SetObscuredRevealIndex(5); - const char16 valid_expect_5_and_6[] = {'*', '*', '*', '*', 0xD800, 0xDC00, 0}; + const base::char16 valid_expect_5_and_6[] = + {'*', '*', '*', '*', 0xD800, 0xDC00, 0}; EXPECT_EQ(valid_expect_5_and_6, render_text->GetLayoutText()); render_text->RenderText::SetObscuredRevealIndex(6); EXPECT_EQ(valid_expect_5_and_6, render_text->GetLayoutText()); } +TEST_F(RenderTextTest, ElidedText) { + // TODO(skanuj) : Add more test cases for following + // - RenderText styles. + // - Cross interaction of truncate, elide and obscure. + // - ElideText tests from text_elider.cc. + struct { + const wchar_t* text; + const wchar_t* layout_text; + const bool elision_expected; + } cases[] = { + // Strings shorter than the elision width should be laid out in full. + { L"", L"" , false }, + { L"M", L"" , false }, + { L" . ", L" . " , false }, + { kWeak, kWeak , false }, + { kLtr, kLtr , false }, + { kLtrRtl, kLtrRtl , false }, + { kLtrRtlLtr, kLtrRtlLtr, false }, + { kRtl, kRtl , false }, + { kRtlLtr, kRtlLtr , false }, + { kRtlLtrRtl, kRtlLtrRtl, false }, + // Strings as long as the elision width should be laid out in full. + { L"012ab", L"012ab" , false }, + // Long strings should be elided with an ellipsis appended at the end. + { L"012abc", L"012a\x2026", true }, + { L"012ab" L"\x5d0\x5d1", L"012a\x2026", true }, + { L"012a" L"\x5d1" L"b", L"012a\x2026", true }, + // No RLM marker added as digits (012) have weak directionality. + { L"01" L"\x5d0\x5d1\x5d2", L"01\x5d0\x5d1\x2026", true }, + // RLM marker added as "ab" have strong LTR directionality. + { L"ab" L"\x5d0\x5d1\x5d2", L"ab\x5d0\x5d1\x2026\x200f", true }, + // Complex script is not handled. In this example, the "\x0915\x093f" is a + // compound glyph, but only half of it is elided. + { L"0123\x0915\x093f", L"0123\x0915\x2026", true }, + // Surrogate pairs should be elided reasonably enough. + { L"0\x05e9\x05bc\x05c1\x05b8", L"0\x05e9\x05bc\x05c1\x05b8", false }, + { L"0\x05e9\x05bc\x05c1\x05b8", L"0\x05e9\x05bc\x2026" , true }, + { L"01\x05e9\x05bc\x05c1\x05b8", L"01\x05e9\x2026" , true }, + { L"012\x05e9\x05bc\x05c1\x05b8", L"012\x2026\x200E" , true }, + { L"012\xF0\x9D\x84\x9E", L"012\xF0\x2026" , true }, + }; + + scoped_ptr<RenderText> expected_render_text(RenderText::CreateInstance()); + expected_render_text->SetFontList(FontList("serif, Sans serif, 12px")); + expected_render_text->SetDisplayRect(Rect(0, 0, 9999, 100)); + + scoped_ptr<RenderText> render_text(RenderText::CreateInstance()); + render_text->SetFontList(FontList("serif, Sans serif, 12px")); + render_text->SetElideBehavior(ELIDE_TAIL); + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { + // Compute expected width + expected_render_text->SetText(WideToUTF16(cases[i].layout_text)); + int expected_width = expected_render_text->GetContentWidth(); + + base::string16 input = WideToUTF16(cases[i].text); + // Extend the input text to ensure that it is wider than the layout_text, + // and so it will get elided. + if (cases[i].elision_expected) + input.append(WideToUTF16(L" MMMMMMMMMMM")); + + render_text->SetText(input); + render_text->SetDisplayRect(Rect(0, 0, expected_width, 100)); + EXPECT_EQ(input, render_text->text()) + << "->For case " << i << ": " << cases[i].text << "\n"; + EXPECT_EQ(WideToUTF16(cases[i].layout_text), render_text->GetLayoutText()) + << "->For case " << i << ": " << cases[i].text << "\n"; + expected_render_text->SetText(base::string16()); + } +} + +TEST_F(RenderTextTest, ElidedObscuredText) { + scoped_ptr<RenderText> expected_render_text(RenderText::CreateInstance()); + expected_render_text->SetFontList(FontList("serif, Sans serif, 12px")); + expected_render_text->SetDisplayRect(Rect(0, 0, 9999, 100)); + expected_render_text->SetText(WideToUTF16(L"**\x2026")); + + scoped_ptr<RenderText> render_text(RenderText::CreateInstance()); + render_text->SetFontList(FontList("serif, Sans serif, 12px")); + render_text->SetElideBehavior(ELIDE_TAIL); + render_text->SetDisplayRect( + Rect(0, 0, expected_render_text->GetContentWidth(), 100)); + render_text->SetObscured(true); + render_text->SetText(WideToUTF16(L"abcdef")); + EXPECT_EQ(WideToUTF16(L"abcdef"), render_text->text()); + EXPECT_EQ(WideToUTF16(L"**\x2026"), render_text->GetLayoutText()); +} + TEST_F(RenderTextTest, TruncatedText) { struct { const wchar_t* text; @@ -742,7 +828,7 @@ TEST_F(RenderTextTest, MoveCursorLeftRight_MeiryoUILigatures) { scoped_ptr<RenderText> render_text(RenderText::CreateInstance()); // Meiryo UI uses single-glyph ligatures for 'ff' and 'ffi', but each letter // (code point) has unique bounds, so mid-glyph cursoring should be possible. - render_text->SetFont(Font("Meiryo UI", 12)); + render_text->SetFontList(FontList("Meiryo UI, 12px")); render_text->SetText(WideToUTF16(L"ff ffi")); EXPECT_EQ(0U, render_text->cursor_position()); for (size_t i = 0; i < render_text->text().length(); ++i) { @@ -806,26 +892,74 @@ TEST_F(RenderTextTest, GraphemePositions) { { kText3, 50, 6, 6 }, }; - // TODO(asvitkine): Disable tests that fail on XP bots due to lack of complete - // font support for some scripts - http://crbug.com/106450 #if defined(OS_WIN) + // TODO(msw): XP fails due to lack of font support: http://crbug.com/106450 if (base::win::GetVersion() < base::win::VERSION_VISTA) return; #endif scoped_ptr<RenderText> render_text(RenderText::CreateInstance()); for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { + SCOPED_TRACE(base::StringPrintf("Testing cases[%" PRIuS "]", i)); render_text->SetText(cases[i].text); size_t next = render_text->IndexOfAdjacentGrapheme(cases[i].index, CURSOR_FORWARD); EXPECT_EQ(cases[i].expected_next, next); - EXPECT_TRUE(render_text->IsCursorablePosition(next)); + EXPECT_TRUE(render_text->IsValidCursorIndex(next)); size_t previous = render_text->IndexOfAdjacentGrapheme(cases[i].index, CURSOR_BACKWARD); EXPECT_EQ(cases[i].expected_previous, previous); - EXPECT_TRUE(render_text->IsCursorablePosition(previous)); + EXPECT_TRUE(render_text->IsValidCursorIndex(previous)); + } +} + +TEST_F(RenderTextTest, MidGraphemeSelectionBounds) { +#if defined(OS_WIN) + // TODO(msw): XP fails due to lack of font support: http://crbug.com/106450 + if (base::win::GetVersion() < base::win::VERSION_VISTA) + return; +#endif + + // Test that selection bounds may be set amid multi-character graphemes. + const base::string16 kHindi = WideToUTF16(L"\x0915\x093f"); + const base::string16 kThai = WideToUTF16(L"\x0e08\x0e33"); + const base::string16 cases[] = { kHindi, kThai }; + + scoped_ptr<RenderText> render_text(RenderText::CreateInstance()); + for (size_t i = 0; i < arraysize(cases); i++) { + SCOPED_TRACE(base::StringPrintf("Testing cases[%" PRIuS "]", i)); + render_text->SetText(cases[i]); + EXPECT_TRUE(render_text->IsValidLogicalIndex(1)); +#if !defined(OS_MACOSX) + EXPECT_FALSE(render_text->IsValidCursorIndex(1)); +#endif + EXPECT_TRUE(render_text->SelectRange(Range(2, 1))); + EXPECT_EQ(Range(2, 1), render_text->selection()); + EXPECT_EQ(1U, render_text->cursor_position()); + // Although selection bounds may be set within a multi-character grapheme, + // cursor movement (e.g. via arrow key) should avoid those indices. + render_text->MoveCursor(CHARACTER_BREAK, CURSOR_LEFT, false); + EXPECT_EQ(0U, render_text->cursor_position()); + render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, false); + EXPECT_EQ(2U, render_text->cursor_position()); + } +} + +TEST_F(RenderTextTest, FindCursorPosition) { + const wchar_t* kTestStrings[] = { kLtrRtl, kLtrRtlLtr, kRtlLtr, kRtlLtrRtl }; + scoped_ptr<RenderText> render_text(RenderText::CreateInstance()); + render_text->SetDisplayRect(Rect(0, 0, 100, 20)); + for (size_t i = 0; i < arraysize(kTestStrings); ++i) { + SCOPED_TRACE(base::StringPrintf("Testing case[%" PRIuS "]", i)); + render_text->SetText(WideToUTF16(kTestStrings[i])); + for(size_t j = 0; j < render_text->text().length(); ++j) { + const Range range(render_text->GetGlyphBounds(j)); + // Test a point just inside the leading edge of the glyph bounds. + int x = range.is_reversed() ? range.GetMax() - 1 : range.GetMin() + 1; + EXPECT_EQ(j, render_text->FindCursorPosition(Point(x, 0)).caret_pos()); + } } } @@ -855,9 +989,8 @@ TEST_F(RenderTextTest, EdgeSelectionModels) { { kHebrewLatin, base::i18n::RIGHT_TO_LEFT }, }; - // TODO(asvitkine): Disable tests that fail on XP bots due to lack of complete - // font support for some scripts - http://crbug.com/106450 #if defined(OS_WIN) + // TODO(msw): XP fails due to lack of font support: http://crbug.com/106450 if (base::win::GetVersion() < base::win::VERSION_VISTA) return; #endif @@ -1210,13 +1343,6 @@ TEST_F(RenderTextTest, StringSizeRespectsFontListMetrics) { EXPECT_EQ(font_list.GetBaseline(), render_text->GetBaseline()); } -TEST_F(RenderTextTest, SetFont) { - scoped_ptr<RenderText> render_text(RenderText::CreateInstance()); - render_text->SetFont(Font("Arial", 12)); - EXPECT_EQ("Arial", render_text->GetPrimaryFont().GetFontName()); - EXPECT_EQ(12, render_text->GetPrimaryFont().GetFontSize()); -} - TEST_F(RenderTextTest, SetFontList) { scoped_ptr<RenderText> render_text(RenderText::CreateInstance()); render_text->SetFontList(FontList("Arial,Symbol, 13px")); @@ -1224,7 +1350,7 @@ TEST_F(RenderTextTest, SetFontList) { ASSERT_EQ(2U, fonts.size()); EXPECT_EQ("Arial", fonts[0].GetFontName()); EXPECT_EQ("Symbol", fonts[1].GetFontName()); - EXPECT_EQ(13, render_text->GetPrimaryFont().GetFontSize()); + EXPECT_EQ(13, render_text->font_list().GetFontSize()); } TEST_F(RenderTextTest, StringSizeBoldWidth) { @@ -1254,20 +1380,20 @@ TEST_F(RenderTextTest, StringSizeHeight) { WideToUTF16(L"\x05e0\x05b8"), // Hebrew }; - Font default_font; - Font larger_font = default_font.DeriveFont(24, default_font.GetStyle()); - EXPECT_GT(larger_font.GetHeight(), default_font.GetHeight()); + const FontList default_font_list; + const FontList& larger_font_list = default_font_list.DeriveWithSizeDelta(24); + EXPECT_GT(larger_font_list.GetHeight(), default_font_list.GetHeight()); for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { scoped_ptr<RenderText> render_text(RenderText::CreateInstance()); - render_text->SetFont(default_font); + render_text->SetFontList(default_font_list); render_text->SetText(cases[i]); const int height1 = render_text->GetStringSize().height(); EXPECT_GT(height1, 0); // Check that setting the larger font increases the height. - render_text->SetFont(larger_font); + render_text->SetFontList(larger_font_list); const int height2 = render_text->GetStringSize().height(); EXPECT_GT(height2, height1); } @@ -1364,8 +1490,8 @@ TEST_F(RenderTextTest, GetTextOffsetHorizontalDefaultInRTL) { TEST_F(RenderTextTest, SameFontForParentheses) { struct { - const char16 left_char; - const char16 right_char; + const base::char16 left_char; + const base::char16 right_char; } punctuation_pairs[] = { { '(', ')' }, { '{', '}' }, @@ -1642,11 +1768,7 @@ TEST_F(RenderTextTest, DisplayRectShowsCursorRTL) { // Changing colors between or inside ligated glyphs should not break shaping. TEST_F(RenderTextTest, SelectionKeepsLigatures) { - const wchar_t* kTestStrings[] = { - L"\x644\x623", - L"\x633\x627" - }; - + const wchar_t* kTestStrings[] = { L"\x644\x623", L"\x633\x627" }; scoped_ptr<RenderText> render_text(RenderText::CreateInstance()); render_text->set_selection_color(SK_ColorRED); Canvas canvas; @@ -1656,8 +1778,7 @@ TEST_F(RenderTextTest, SelectionKeepsLigatures) { const int expected_width = render_text->GetStringSize().width(); render_text->MoveCursorTo(SelectionModel(Range(0, 1), CURSOR_FORWARD)); EXPECT_EQ(expected_width, render_text->GetStringSize().width()); - // Draw the text. It shouldn't hit any DCHECKs or crash. - // See http://crbug.com/214150 + // Drawing the text should not DCHECK or crash; see http://crbug.com/262119 render_text->Draw(&canvas); render_text->MoveCursorTo(SelectionModel(0, CURSOR_FORWARD)); } @@ -1782,7 +1903,6 @@ TEST_F(RenderTextTest, Multiline_Newline) { } } - TEST_F(RenderTextTest, Win_BreakRunsByUnicodeBlocks) { scoped_ptr<RenderTextWin> render_text( static_cast<RenderTextWin*>(RenderText::CreateInstance())); @@ -1804,4 +1924,120 @@ TEST_F(RenderTextTest, Win_BreakRunsByUnicodeBlocks) { } #endif // defined(OS_WIN) +TEST_F(RenderTextTest, HarfBuzz_CharToGlyph) { + struct { + uint32 glyph_to_char[4]; + size_t char_to_glyph_expected[4]; + Range char_range_to_glyph_range_expected[4]; + bool is_rtl; + } cases[] = { + { // From string "A B C D" to glyphs "a b c d". + { 0, 1, 2, 3 }, + { 0, 1, 2, 3 }, + { Range(0, 1), Range(1, 2), Range(2, 3), Range(3, 4) }, + false + }, + { // From string "A B C D" to glyphs "d b c a". + { 3, 2, 1, 0 }, + { 3, 2, 1, 0 }, + { Range(3, 4), Range(2, 3), Range(1, 2), Range(0, 1) }, + true + }, + { // From string "A B C D" to glyphs "ab c c d". + { 0, 2, 2, 3 }, + { 0, 0, 1, 3 }, + { Range(0, 1), Range(0, 1), Range(1, 3), Range(3, 4) }, + false + }, + { // From string "A B C D" to glyphs "d c c ba". + { 3, 2, 2, 0 }, + { 3, 3, 1, 0 }, + { Range(3, 4), Range(3, 4), Range(1, 3), Range(0, 1) }, + true + }, + }; + + internal::TextRunHarfBuzz run; + run.range = Range(0, 4); + run.glyph_count = 4; + run.glyph_to_char.reset(new uint32[4]); + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { + std::copy(cases[i].glyph_to_char, cases[i].glyph_to_char + 4, + run.glyph_to_char.get()); + run.is_rtl = cases[i].is_rtl; + for (size_t j = 0; j < 4; ++j) { + SCOPED_TRACE(base::StringPrintf("Case %" PRIuS ", char %" PRIuS, i, j)); + EXPECT_EQ(cases[i].char_to_glyph_expected[j], run.CharToGlyph(j)); + EXPECT_EQ(cases[i].char_range_to_glyph_range_expected[j], + run.CharRangeToGlyphRange(Range(j, j + 1))); + } + } +} + +TEST_F(RenderTextTest, HarfBuzz_RunDirection) { + RenderTextHarfBuzz render_text; + const base::string16 mixed = + WideToUTF16(L"\x05D0\x05D1" L"1234" L"\x05D2\x05D3"); + render_text.SetText(mixed); + render_text.EnsureLayout(); + ASSERT_EQ(3U, render_text.runs_.size()); + EXPECT_TRUE(render_text.runs_[0]->is_rtl); + EXPECT_FALSE(render_text.runs_[1]->is_rtl); + EXPECT_TRUE(render_text.runs_[2]->is_rtl); +} + +TEST_F(RenderTextTest, HarfBuzz_BreakRunsByUnicodeBlocks) { + RenderTextHarfBuzz render_text; + + // The '\x25B6' "play character" should break runs. http://crbug.com/278913 + render_text.SetText(WideToUTF16(L"x\x25B6y")); + render_text.EnsureLayout(); + ASSERT_EQ(3U, render_text.runs_.size()); + EXPECT_EQ(Range(0, 1), render_text.runs_[0]->range); + EXPECT_EQ(Range(1, 2), render_text.runs_[1]->range); + EXPECT_EQ(Range(2, 3), render_text.runs_[2]->range); + + render_text.SetText(WideToUTF16(L"x \x25B6 y")); + render_text.EnsureLayout(); + ASSERT_EQ(3U, render_text.runs_.size()); + EXPECT_EQ(Range(0, 2), render_text.runs_[0]->range); + EXPECT_EQ(Range(2, 3), render_text.runs_[1]->range); + EXPECT_EQ(Range(3, 5), render_text.runs_[2]->range); +} + +// Disabled on Mac because RenderTextMac doesn't implement GetGlyphBounds. +#if !defined(OS_MACOSX) +TEST_F(RenderTextTest, GlyphBounds) { + const wchar_t* kTestStrings[] = { + L"asdf 1234 qwer", L"\x0647\x0654", L"\x0645\x0631\x062D\x0628\x0627" + }; + scoped_ptr<RenderText> render_text(RenderText::CreateInstance()); + + for (size_t i = 0; i < arraysize(kTestStrings); ++i) { + render_text->SetText(WideToUTF16(kTestStrings[i])); + render_text->EnsureLayout(); + + for (size_t j = 0; j < render_text->text().length(); ++j) + EXPECT_FALSE(render_text->GetGlyphBounds(j).is_empty()); + } +} +#endif + +// Remove this after making RTHB default in favor of RenderTextTest.GlyphBounds. +TEST_F(RenderTextTest, HarfBuzz_GlyphBounds) { + const wchar_t* kTestStrings[] = { + L"asdf 1234 qwer", L"\x0647\x0654", L"\x0645\x0631\x062D\x0628\x0627" + }; + scoped_ptr<RenderText> render_text(new RenderTextHarfBuzz); + + for (size_t i = 0; i < arraysize(kTestStrings); ++i) { + render_text->SetText(WideToUTF16(kTestStrings[i])); + render_text->EnsureLayout(); + + for (size_t j = 0; j < render_text->text().length(); ++j) + EXPECT_FALSE(render_text->GetGlyphBounds(j).is_empty()); + } +} + } // namespace gfx diff --git a/chromium/ui/gfx/render_text_win.cc b/chromium/ui/gfx/render_text_win.cc index 4afbc7fa71e..f79577fde0a 100644 --- a/chromium/ui/gfx/render_text_win.cc +++ b/chromium/ui/gfx/render_text_win.cc @@ -86,7 +86,8 @@ bool ChooseFallbackFont(HDC hdc, log_font.lfFaceName[0] = 0; EnumEnhMetaFile(0, meta_file, MetaFileEnumProc, &log_font, NULL); if (log_font.lfFaceName[0]) { - *result = Font(UTF16ToUTF8(log_font.lfFaceName), font.GetFontSize()); + *result = Font(base::UTF16ToUTF8(log_font.lfFaceName), + font.GetFontSize()); found_fallback = true; } } @@ -117,11 +118,11 @@ void DeriveFontIfNecessary(int font_size, const int current_style = (font->GetStyle() & kStyleMask); const int current_size = font->GetFontSize(); if (current_style != target_style || current_size != font_size) - *font = font->DeriveFont(font_size - current_size, target_style); + *font = font->Derive(font_size - current_size, target_style); } // Returns true if |c| is a Unicode BiDi control character. -bool IsUnicodeBidiControlCharacter(char16 c) { +bool IsUnicodeBidiControlCharacter(base::char16 c) { return c == base::i18n::kRightToLeftMark || c == base::i18n::kLeftToRightMark || c == base::i18n::kLeftToRightEmbeddingMark || @@ -263,6 +264,26 @@ bool IsUnusualBlockCode(const UBlockCode block_code) { block_code == UBLOCK_MISCELLANEOUS_SYMBOLS; } +// Returns the index of the first unusual character after a usual character or +// vice versa. Unusual characters are defined by |IsUnusualBlockCode|. +size_t FindUnusualCharacter(const base::string16& text, + size_t run_start, + size_t run_break) { + const int32 run_length = static_cast<int32>(run_break - run_start); + base::i18n::UTF16CharIterator iter(text.c_str() + run_start, + run_length); + const UBlockCode first_block_code = ublock_getCode(iter.get()); + const bool first_block_unusual = IsUnusualBlockCode(first_block_code); + while (iter.Advance() && iter.array_pos() < run_length) { + const UBlockCode current_block_code = ublock_getCode(iter.get()); + if (current_block_code != first_block_code && + (first_block_unusual || IsUnusualBlockCode(current_block_code))) { + return run_start + iter.array_pos(); + } + } + return run_break; +} + } // namespace namespace internal { @@ -503,19 +524,14 @@ HDC RenderTextWin::cached_hdc_ = NULL; // static std::map<std::string, Font> RenderTextWin::successful_substitute_fonts_; -RenderTextWin::RenderTextWin() - : RenderText(), - needs_layout_(false) { +RenderTextWin::RenderTextWin() : RenderText(), needs_layout_(false) { set_truncate_length(kMaxUniscribeTextLength); - memset(&script_control_, 0, sizeof(script_control_)); memset(&script_state_, 0, sizeof(script_state_)); - MoveCursorTo(EdgeSelectionModel(CURSOR_LEFT)); } -RenderTextWin::~RenderTextWin() { -} +RenderTextWin::~RenderTextWin() {} Size RenderTextWin::GetStringSize() { EnsureLayout(); @@ -662,6 +678,7 @@ SelectionModel RenderTextWin::AdjacentWordSelectionModel( } Range RenderTextWin::GetGlyphBounds(size_t index) { + EnsureLayout(); const size_t run_index = GetRunContainingCaret(SelectionModel(index, CURSOR_FORWARD)); // Return edge bounds if the index is invalid or beyond the layout text size. @@ -715,7 +732,7 @@ std::vector<Rect> RenderTextWin::GetSubstringBounds(const Range& range) { size_t RenderTextWin::TextIndexToLayoutIndex(size_t index) const { DCHECK_LE(index, text().length()); - ptrdiff_t i = obscured() ? gfx::UTF16IndexToOffset(text(), 0, index) : index; + ptrdiff_t i = obscured() ? UTF16IndexToOffset(text(), 0, index) : index; CHECK_GE(i, 0); // Clamp layout indices to the length of the text actually used for layout. return std::min<size_t>(GetLayoutText().length(), i); @@ -726,24 +743,22 @@ size_t RenderTextWin::LayoutIndexToTextIndex(size_t index) const { return index; DCHECK_LE(index, GetLayoutText().length()); - const size_t text_index = gfx::UTF16OffsetToIndex(text(), 0, index); + const size_t text_index = UTF16OffsetToIndex(text(), 0, index); DCHECK_LE(text_index, text().length()); return text_index; } -bool RenderTextWin::IsCursorablePosition(size_t position) { - if (position == 0 || position == text().length()) +bool RenderTextWin::IsValidCursorIndex(size_t index) { + if (index == 0 || index == text().length()) return true; + if (!IsValidLogicalIndex(index)) + return false; EnsureLayout(); - - // Check that the index is at a valid code point (not mid-surrgate-pair), - // that it is not truncated from layout text (its glyph is shown on screen), - // and that its glyph has distinct bounds (not mid-multi-character-grapheme). - // An example of a multi-character-grapheme that is not a surrogate-pair is: - // \x0915\x093f - (ki) - one of many Devanagari biconsonantal conjuncts. - return gfx::IsValidCodePointIndex(text(), position) && - position < LayoutIndexToTextIndex(GetLayoutText().length()) && - GetGlyphBounds(position) != GetGlyphBounds(position - 1); + // Disallow indices amid multi-character graphemes by checking glyph bounds. + // These characters are not surrogate-pairs, but may yield a single glyph: + // \x0915\x093f - (ki) - one of many Devanagari biconsonantal conjuncts. + // \x0e08\x0e33 - (cho chan + sara am) - a Thai consonant and vowel pair. + return GetGlyphBounds(index) != GetGlyphBounds(index - 1); } void RenderTextWin::ResetLayout() { @@ -799,7 +814,8 @@ void RenderTextWin::DrawVisualText(Canvas* canvas) { GetCachedFontSmoothingSettings(&smoothing_enabled, &cleartype_enabled); // Note that |cleartype_enabled| corresponds to Skia's |enable_lcd_text|. renderer.SetFontSmoothingSettings( - smoothing_enabled, cleartype_enabled && !background_is_transparent()); + smoothing_enabled, cleartype_enabled && !background_is_transparent(), + smoothing_enabled /* subpixel_positioning */); ApplyCompositionAndSelectionStyles(); @@ -841,7 +857,7 @@ void RenderTextWin::DrawVisualText(Canvas* canvas) { for (size_t k = glyph_range.start(); k < glyph_range.end(); ++k) { pos[k - glyph_range.start()].set( SkIntToScalar(text_offset.x() + run->offsets[k].du + segment_x), - SkIntToScalar(text_offset.y() + run->offsets[k].dv)); + SkIntToScalar(text_offset.y() - run->offsets[k].dv)); segment_x += run->advance_widths[k]; } pos.back().set(SkIntToScalar(text_offset.x() + segment_x), @@ -858,8 +874,13 @@ void RenderTextWin::DrawVisualText(Canvas* canvas) { const Range intersection = colors().GetRange(it).Intersect(segment->char_range); const Range colored_glyphs = CharRangeToGlyphRange(*run, intersection); + // The range may be empty if a portion of a multi-character grapheme is + // selected, yielding two colors for a single glyph. For now, this just + // paints the glyph with a single style, but it should paint it twice, + // clipped according to selection bounds. See http://crbug.com/366786 + if (colored_glyphs.is_empty()) + continue; DCHECK(glyph_range.Contains(colored_glyphs)); - DCHECK(!colored_glyphs.is_empty()); const SkPoint& start_pos = pos[colored_glyphs.start() - glyph_range.start()]; const SkPoint& end_pos = @@ -876,6 +897,8 @@ void RenderTextWin::DrawVisualText(Canvas* canvas) { preceding_segment_widths += segment_width; } + + renderer.EndDiagonalStrike(); } UndoCompositionAndSelectionStyles(); @@ -926,7 +949,7 @@ void RenderTextWin::ItemizeLogicalText() { for (size_t run_break = 0; run_break < layout_text_length;) { internal::TextRun* run = new internal::TextRun(); run->range.set_start(run_break); - run->font = GetPrimaryFont(); + run->font = font_list().GetPrimaryFont(); run->font_style = (style.style(BOLD) ? Font::BOLD : 0) | (style.style(ITALIC) ? Font::ITALIC : 0); DeriveFontIfNecessary(run->font.GetFontSize(), run->font.GetHeight(), @@ -952,20 +975,8 @@ void RenderTextWin::ItemizeLogicalText() { // This avoids using their fallback fonts for more characters than needed, // in cases like "\x25B6 Media Title", etc. http://crbug.com/278913 if (run_break > run->range.start()) { - const size_t run_start = run->range.start(); - const int32 run_length = static_cast<int32>(run_break - run_start); - base::i18n::UTF16CharIterator iter(layout_text.c_str() + run_start, - run_length); - const UBlockCode first_block_code = ublock_getCode(iter.get()); - const bool first_block_unusual = IsUnusualBlockCode(first_block_code); - while (iter.Advance() && iter.array_pos() < run_length) { - const UBlockCode current_block_code = ublock_getCode(iter.get()); - if (current_block_code != first_block_code && - (first_block_unusual || IsUnusualBlockCode(current_block_code))) { - run_break = run_start + iter.array_pos(); - break; - } - } + run_break = + FindUnusualCharacter(layout_text, run->range.start(), run_break); } DCHECK(IsValidCodePointIndex(layout_text, run_break)); @@ -1271,7 +1282,7 @@ SelectionModel RenderTextWin::LastSelectionModelInsideRun( return SelectionModel(position, CURSOR_FORWARD); } -RenderText* RenderText::CreateInstance() { +RenderText* RenderText::CreateNativeInstance() { return new RenderTextWin; } diff --git a/chromium/ui/gfx/render_text_win.h b/chromium/ui/gfx/render_text_win.h index 1a1ba4805cb..4e8caf6484d 100644 --- a/chromium/ui/gfx/render_text_win.h +++ b/chromium/ui/gfx/render_text_win.h @@ -80,7 +80,7 @@ class RenderTextWin : public RenderText { virtual std::vector<Rect> GetSubstringBounds(const Range& range) OVERRIDE; virtual size_t TextIndexToLayoutIndex(size_t index) const OVERRIDE; virtual size_t LayoutIndexToTextIndex(size_t index) const OVERRIDE; - virtual bool IsCursorablePosition(size_t position) OVERRIDE; + virtual bool IsValidCursorIndex(size_t index) OVERRIDE; virtual void ResetLayout() OVERRIDE; virtual void EnsureLayout() OVERRIDE; virtual void DrawVisualText(Canvas* canvas) OVERRIDE; diff --git a/chromium/ui/gfx/safe_integer_conversions.h b/chromium/ui/gfx/safe_integer_conversions.h index 523b1f3441f..ea9fde726a8 100644 --- a/chromium/ui/gfx/safe_integer_conversions.h +++ b/chromium/ui/gfx/safe_integer_conversions.h @@ -1,54 +1,7 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_GFX_SAFE_INTEGER_CONVERSIONS_H_ -#define UI_GFX_SAFE_INTEGER_CONVERSIONS_H_ +// TODO(beng): remove once callsites are patched. +#include "ui/gfx/geometry/safe_integer_conversions.h" -#include <cmath> -#include <limits> - -#include "ui/gfx/gfx_export.h" - -namespace gfx { - -inline int ClampToInt(float value) { - if (value != value) - return 0; // no int NaN. - if (value >= std::numeric_limits<int>::max()) - return std::numeric_limits<int>::max(); - if (value <= std::numeric_limits<int>::min()) - return std::numeric_limits<int>::min(); - return static_cast<int>(value); -} - -inline int ToFlooredInt(float value) { - return ClampToInt(std::floor(value)); -} - -inline int ToCeiledInt(float value) { - return ClampToInt(std::ceil(value)); -} - -inline int ToRoundedInt(float value) { - float rounded; - if (value >= 0.0f) - rounded = std::floor(value + 0.5f); - else - rounded = std::ceil(value - 0.5f); - return ClampToInt(rounded); -} - -inline bool IsExpressibleAsInt(float value) { - if (value != value) - return false; // no int NaN. - if (value > std::numeric_limits<int>::max()) - return false; - if (value < std::numeric_limits<int>::min()) - return false; - return true; -} - -} // namespace gfx - -#endif // UI_GFX_SAFE_INTEGER_CONVERSIONS_H_ diff --git a/chromium/ui/gfx/scoped_gobject.h b/chromium/ui/gfx/scoped_gobject.h deleted file mode 100644 index 860849ec84e..00000000000 --- a/chromium/ui/gfx/scoped_gobject.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_GFX_SCOPED_GOBJECT_H_ -#define UI_GFX_SCOPED_GOBJECT_H_ - -#include <glib-object.h> - -#include "base/memory/scoped_ptr.h" - -namespace ui { - -// It's not legal C++ to have a templatized typedefs, so we wrap it in a -// struct. When using this, you need to include ::Type. E.g., -// ScopedGObject<GdkPixbufLoader>::Type loader(gdk_pixbuf_loader_new()); -template<class T> -struct ScopedGObject { - // A helper class that will g_object_unref |p| when it goes out of scope. - // This never adds a ref, it only unrefs. - template<class U> - struct GObjectUnrefer { - void operator()(U* ptr) const { - if (ptr) - g_object_unref(ptr); - } - }; - - typedef scoped_ptr_malloc<T, GObjectUnrefer<T> > Type; -}; - -} // namespace ui - -#endif // UI_GFX_SCOPED_GOBJECT_H_ diff --git a/chromium/ui/gfx/screen_android.cc b/chromium/ui/gfx/screen_android.cc index e90bb76725d..9114b855453 100644 --- a/chromium/ui/gfx/screen_android.cc +++ b/chromium/ui/gfx/screen_android.cc @@ -43,6 +43,7 @@ class ScreenAndroid : public Screen { gfx::Display display(0, bounds_in_dip); if (!gfx::Display::HasForceDeviceScaleFactor()) display.set_device_scale_factor(device_scale_factor); + display.SetRotationAsDegree(device_info.GetRotationDegrees()); return display; } diff --git a/chromium/ui/gfx/screen_gtk.cc b/chromium/ui/gfx/screen_gtk.cc deleted file mode 100644 index ac2464bbbd7..00000000000 --- a/chromium/ui/gfx/screen_gtk.cc +++ /dev/null @@ -1,210 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/gfx/screen.h" - -#include <gdk/gdkx.h> -#include <gtk/gtk.h> - -#include "base/logging.h" -#include "ui/gfx/display.h" - -namespace { - -bool GetScreenWorkArea(gfx::Rect* out_rect) { - gboolean ok; - guchar* raw_data = NULL; - gint data_len = 0; - ok = gdk_property_get(gdk_get_default_root_window(), // a gdk window - gdk_atom_intern("_NET_WORKAREA", FALSE), // property - gdk_atom_intern("CARDINAL", FALSE), // property type - 0, // byte offset into property - 0xff, // property length to retrieve - false, // delete property after retrieval? - NULL, // returned property type - NULL, // returned data format - &data_len, // returned data len - &raw_data); // returned data - if (!ok) - return false; - - // We expect to get four longs back: x, y, width, height. - if (data_len < static_cast<gint>(4 * sizeof(glong))) { - NOTREACHED(); - g_free(raw_data); - return false; - } - - glong* data = reinterpret_cast<glong*>(raw_data); - gint x = data[0]; - gint y = data[1]; - gint width = data[2]; - gint height = data[3]; - g_free(raw_data); - - out_rect->SetRect(x, y, width, height); - return true; -} - -gfx::Display GetDisplayForMonitorNum(GdkScreen* screen, gint monitor_num) { - GdkRectangle bounds; - gdk_screen_get_monitor_geometry(screen, monitor_num, &bounds); - // Use |monitor_num| as display id. - gfx::Display display(monitor_num, gfx::Rect(bounds)); - if (gdk_screen_get_primary_monitor(screen) == monitor_num) { - gfx::Rect rect; - if (GetScreenWorkArea(&rect)) - display.set_work_area(gfx::IntersectRects(rect, display.bounds())); - } - return display; -} - -gfx::Display GetMonitorAreaNearestWindow(gfx::NativeView view) { - GdkScreen* screen = gdk_screen_get_default(); - gint monitor_num = 0; - if (view && GTK_IS_WINDOW(view)) { - GtkWidget* top_level = gtk_widget_get_toplevel(view); - DCHECK(GTK_IS_WINDOW(top_level)); - GtkWindow* window = GTK_WINDOW(top_level); - screen = gtk_window_get_screen(window); - monitor_num = gdk_screen_get_monitor_at_window( - screen, - gtk_widget_get_window(top_level)); - } - return GetDisplayForMonitorNum(screen, monitor_num); -} - -class ScreenGtk : public gfx::Screen { - public: - ScreenGtk() { - } - - virtual ~ScreenGtk() { - } - - virtual bool IsDIPEnabled() OVERRIDE { - return false; - } - - virtual gfx::Point GetCursorScreenPoint() OVERRIDE { - gint x, y; - gdk_display_get_pointer(gdk_display_get_default(), NULL, &x, &y, NULL); - return gfx::Point(x, y); - } - - // Returns the window under the cursor. - virtual gfx::NativeWindow GetWindowUnderCursor() OVERRIDE { - GdkWindow* window = gdk_window_at_pointer(NULL, NULL); - if (!window) - return NULL; - - gpointer data = NULL; - gdk_window_get_user_data(window, &data); - GtkWidget* widget = reinterpret_cast<GtkWidget*>(data); - if (!widget) - return NULL; - widget = gtk_widget_get_toplevel(widget); - return GTK_IS_WINDOW(widget) ? GTK_WINDOW(widget) : NULL; - } - - virtual gfx::NativeWindow GetWindowAtScreenPoint(const gfx::Point& point) - OVERRIDE { - NOTIMPLEMENTED(); - return NULL; - } - - // Returns the number of displays. - // Mirrored displays are excluded; this method is intended to return the - // number of distinct, usable displays. - virtual int GetNumDisplays() const OVERRIDE { - // This query is kinda bogus for Linux -- do we want number of X screens? - // The number of monitors Xinerama has? We'll just use whatever GDK uses. - GdkScreen* screen = gdk_screen_get_default(); - return gdk_screen_get_n_monitors(screen); - } - - virtual std::vector<gfx::Display> GetAllDisplays() const OVERRIDE { - GdkScreen* screen = gdk_screen_get_default(); - gint num_of_displays = gdk_screen_get_n_monitors(screen); - std::vector<gfx::Display> all_displays; - for (gint i = 0; i < num_of_displays; ++i) - all_displays.push_back(GetDisplayForMonitorNum(screen, i)); - return all_displays; - } - - // Returns the display nearest the specified window. - virtual gfx::Display GetDisplayNearestWindow( - gfx::NativeView view) const OVERRIDE { - // Do not use the _NET_WORKAREA here, this is supposed to be an area on a - // specific monitor, and _NET_WORKAREA is a hint from the WM that - // generally spans across all monitors. This would make the work area - // larger than the monitor. - // TODO(danakj) This is a work-around as there is no standard way to get - // this area, but it is a rect that we should be computing. The standard - // means to compute this rect would be to watch all windows with - // _NET_WM_STRUT(_PARTIAL) hints, and subtract their space from the - // physical area of the display to construct a work area. - // TODO(oshima): Implement Observer. - return GetMonitorAreaNearestWindow(view); - } - - // Returns the the display nearest the specified point. - virtual gfx::Display GetDisplayNearestPoint( - const gfx::Point& point) const OVERRIDE { - GdkScreen* screen = gdk_screen_get_default(); - gint monitor = gdk_screen_get_monitor_at_point( - screen, point.x(), point.y()); - // TODO(oshima): Implement Observer. - return GetDisplayForMonitorNum(screen, monitor); - } - - // Returns the display that most closely intersects the provided bounds. - virtual gfx::Display GetDisplayMatching( - const gfx::Rect& match_rect) const OVERRIDE { - std::vector<gfx::Display> displays = GetAllDisplays(); - gfx::Display maxIntersectDisplay; - gfx::Rect maxIntersection; - for (std::vector<gfx::Display>::iterator it = displays.begin(); - it != displays.end(); ++it) { - gfx::Rect displayIntersection = it->bounds(); - displayIntersection.Intersect(match_rect); - if (displayIntersection.size().GetArea() > - maxIntersection.size().GetArea()) { - maxIntersectDisplay = *it; - maxIntersection = displayIntersection; - } - } - return maxIntersectDisplay.is_valid() ? - maxIntersectDisplay : GetPrimaryDisplay(); - } - - // Returns the primary display. - virtual gfx::Display GetPrimaryDisplay() const OVERRIDE { - GdkScreen* screen = gdk_screen_get_default(); - gint primary_monitor_index = gdk_screen_get_primary_monitor(screen); - // TODO(oshima): Implement Observer. - return GetDisplayForMonitorNum(screen, primary_monitor_index); - } - - virtual void AddObserver(gfx::DisplayObserver* observer) OVERRIDE { - // TODO(oshima): crbug.com/122863. - } - - virtual void RemoveObserver(gfx::DisplayObserver* observer) OVERRIDE { - // TODO(oshima): crbug.com/122863. - } - - private: - DISALLOW_COPY_AND_ASSIGN(ScreenGtk); -}; - -} // namespace - -namespace gfx { - -Screen* CreateNativeScreen() { - return new ScreenGtk; -} - -} // namespace gfx diff --git a/chromium/ui/gfx/screen_ios.mm b/chromium/ui/gfx/screen_ios.mm index a2770298ca8..f630f00a9f0 100644 --- a/chromium/ui/gfx/screen_ios.mm +++ b/chromium/ui/gfx/screen_ios.mm @@ -69,7 +69,8 @@ class ScreenIos : public gfx::Screen { // Returns the primary display. virtual gfx::Display GetPrimaryDisplay() const OVERRIDE { - UIScreen* mainScreen = [[UIScreen screens] objectAtIndex:0]; + UIScreen* mainScreen = [UIScreen mainScreen]; + CHECK(mainScreen); gfx::Display display(0, gfx::Rect(mainScreen.bounds)); display.set_device_scale_factor([mainScreen scale]); return display; diff --git a/chromium/ui/gfx/screen_mac.mm b/chromium/ui/gfx/screen_mac.mm index 51c0b665b7e..377c8592210 100644 --- a/chromium/ui/gfx/screen_mac.mm +++ b/chromium/ui/gfx/screen_mac.mm @@ -154,7 +154,10 @@ class ScreenMac : public gfx::Screen { virtual gfx::Display GetDisplayNearestWindow( gfx::NativeView view) const OVERRIDE { - NSWindow* window = [view window]; + NSWindow* window = nil; +#if !defined(USE_AURA) + window = [view window]; +#endif if (!window) return GetPrimaryDisplay(); NSScreen* match_screen = [window screen]; @@ -209,8 +212,10 @@ class ScreenMac : public gfx::Screen { namespace gfx { +#if !defined(USE_AURA) Screen* CreateNativeScreen() { return new ScreenMac; } +#endif } diff --git a/chromium/ui/gfx/screen_win.cc b/chromium/ui/gfx/screen_win.cc index 7903cc5fdd5..4530642e24b 100644 --- a/chromium/ui/gfx/screen_win.cc +++ b/chromium/ui/gfx/screen_win.cc @@ -19,13 +19,14 @@ MONITORINFOEX GetMonitorInfoForMonitor(HMONITOR monitor) { MONITORINFOEX monitor_info; ZeroMemory(&monitor_info, sizeof(MONITORINFOEX)); monitor_info.cbSize = sizeof(monitor_info); - base::win::GetMonitorInfoWrapper(monitor, &monitor_info); + GetMonitorInfo(monitor, &monitor_info); return monitor_info; } gfx::Display GetDisplay(MONITORINFOEX& monitor_info) { // TODO(oshima): Implement Observer. - int64 id = static_cast<int64>(base::Hash(WideToUTF8(monitor_info.szDevice))); + int64 id = static_cast<int64>( + base::Hash(base::WideToUTF8(monitor_info.szDevice))); gfx::Rect bounds = gfx::Rect(monitor_info.rcMonitor); gfx::Display display(id, bounds); display.set_work_area(gfx::Rect(monitor_info.rcWork)); @@ -64,7 +65,8 @@ bool ScreenWin::IsDIPEnabled() { gfx::Point ScreenWin::GetCursorScreenPoint() { POINT pt; GetCursorPos(&pt); - return gfx::Point(pt); + gfx::Point cursor_pos_pixels(pt); + return gfx::win::ScreenToDIPPoint(cursor_pos_pixels); } gfx::NativeWindow ScreenWin::GetWindowUnderCursor() { @@ -74,7 +76,8 @@ gfx::NativeWindow ScreenWin::GetWindowUnderCursor() { } gfx::NativeWindow ScreenWin::GetWindowAtScreenPoint(const gfx::Point& point) { - return GetNativeWindowFromHWND(WindowFromPoint(point.ToPOINT())); + gfx::Point point_in_pixels = gfx::win::DIPToScreenPoint(point); + return GetNativeWindowFromHWND(WindowFromPoint(point_in_pixels.ToPOINT())); } int ScreenWin::GetNumDisplays() const { @@ -99,8 +102,8 @@ gfx::Display ScreenWin::GetDisplayNearestWindow(gfx::NativeView window) const { MONITORINFOEX monitor_info; monitor_info.cbSize = sizeof(monitor_info); - base::win::GetMonitorInfoWrapper( - MonitorFromWindow(window_hwnd, MONITOR_DEFAULTTONEAREST), &monitor_info); + GetMonitorInfo(MonitorFromWindow(window_hwnd, MONITOR_DEFAULTTONEAREST), + &monitor_info); return GetDisplay(monitor_info); } @@ -110,7 +113,7 @@ gfx::Display ScreenWin::GetDisplayNearestPoint(const gfx::Point& point) const { MONITORINFOEX mi; ZeroMemory(&mi, sizeof(MONITORINFOEX)); mi.cbSize = sizeof(mi); - if (monitor && base::win::GetMonitorInfoWrapper(monitor, &mi)) { + if (monitor && GetMonitorInfo(monitor, &mi)) { return GetDisplay(mi); } return gfx::Display(); @@ -129,7 +132,7 @@ gfx::Display ScreenWin::GetPrimaryDisplay() const { gfx::Display display = GetDisplay(mi); // TODO(kevers|girard): Test if these checks can be reintroduced for high-DIP // once more of the app is DIP-aware. - if (!IsInHighDPIMode()) { + if (!(IsInHighDPIMode() || IsHighDPIEnabled())) { DCHECK_EQ(GetSystemMetrics(SM_CXSCREEN), display.size().width()); DCHECK_EQ(GetSystemMetrics(SM_CYSCREEN), display.size().height()); } @@ -145,27 +148,13 @@ void ScreenWin::RemoveObserver(DisplayObserver* observer) { } HWND ScreenWin::GetHWNDFromNativeView(NativeView window) const { -#if defined(USE_AURA) NOTREACHED(); return NULL; -#else - return window; -#endif // USE_AURA } NativeWindow ScreenWin::GetNativeWindowFromHWND(HWND hwnd) const { -#if defined(USE_AURA) NOTREACHED(); return NULL; -#else - return hwnd; -#endif // USE_AURA } -#if !defined(USE_AURA) -Screen* CreateNativeScreen() { - return new ScreenWin; -} -#endif // !USE_AURA - } // namespace gfx diff --git a/chromium/ui/gfx/selection_model.h b/chromium/ui/gfx/selection_model.h index d509e898a99..356416df9c3 100644 --- a/chromium/ui/gfx/selection_model.h +++ b/chromium/ui/gfx/selection_model.h @@ -73,19 +73,16 @@ class GFX_EXPORT SelectionModel { size_t caret_pos() const { return selection_.end(); } LogicalCursorDirection caret_affinity() const { return caret_affinity_; } + // WARNING: Generally the selection start should not be changed without + // considering the effect on the caret affinity. + void set_selection_start(size_t pos) { selection_.set_start(pos); } + bool operator==(const SelectionModel& sel) const; bool operator!=(const SelectionModel& sel) const { return !(*this == sel); } std::string ToString() const; private: - friend class RenderText; - - // TODO(benrg): Generally the selection start should not be changed without - // considering the effect on the caret affinity. This setter is exposed only - // to RenderText to discourage misuse, and should probably be removed. - void set_selection_start(size_t pos) { selection_.set_start(pos); } - // Logical selection. The logical caret position is the end of the selection. Range selection_; diff --git a/chromium/ui/gfx/size.h b/chromium/ui/gfx/size.h index c96f5897f3b..78457d5088d 100644 --- a/chromium/ui/gfx/size.h +++ b/chromium/ui/gfx/size.h @@ -1,67 +1,7 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_GFX_SIZE_H_ -#define UI_GFX_SIZE_H_ +// TODO(beng): remove once callsites are patched. +#include "ui/gfx/geometry/size.h" -#include <string> - -#include "base/compiler_specific.h" -#include "ui/gfx/gfx_export.h" -#include "ui/gfx/size_base.h" -#include "ui/gfx/size_f.h" - -#if defined(OS_WIN) -typedef struct tagSIZE SIZE; -#elif defined(OS_IOS) -#include <CoreGraphics/CoreGraphics.h> -#elif defined(OS_MACOSX) -#include <ApplicationServices/ApplicationServices.h> -#endif - -namespace gfx { - -// A size has width and height values. -class GFX_EXPORT Size : public SizeBase<Size, int> { - public: - Size() : SizeBase<Size, int>(0, 0) {} - Size(int width, int height) : SizeBase<Size, int>(width, height) {} -#if defined(OS_MACOSX) - explicit Size(const CGSize& s); -#endif - - ~Size() {} - -#if defined(OS_MACOSX) - Size& operator=(const CGSize& s); -#endif - -#if defined(OS_WIN) - SIZE ToSIZE() const; -#elif defined(OS_MACOSX) - CGSize ToCGSize() const; -#endif - - operator SizeF() const { - return SizeF(width(), height()); - } - - std::string ToString() const; -}; - -inline bool operator==(const Size& lhs, const Size& rhs) { - return lhs.width() == rhs.width() && lhs.height() == rhs.height(); -} - -inline bool operator!=(const Size& lhs, const Size& rhs) { - return !(lhs == rhs); -} - -#if !defined(COMPILER_MSVC) -extern template class SizeBase<Size, int>; -#endif - -} // namespace gfx - -#endif // UI_GFX_SIZE_H_ diff --git a/chromium/ui/gfx/size_conversions.h b/chromium/ui/gfx/size_conversions.h index af68195b55b..0ee67f417e8 100644 --- a/chromium/ui/gfx/size_conversions.h +++ b/chromium/ui/gfx/size_conversions.h @@ -1,24 +1,7 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_GFX_SIZE_CONVERSIONS_H_ -#define UI_GFX_SIZE_CONVERSIONS_H_ +// TODO(beng): remove once callsites are patched. +#include "ui/gfx/geometry/size_conversions.h" -#include "ui/gfx/size.h" -#include "ui/gfx/size_f.h" - -namespace gfx { - -// Returns a Size with each component from the input SizeF floored. -GFX_EXPORT Size ToFlooredSize(const SizeF& size); - -// Returns a Size with each component from the input SizeF ceiled. -GFX_EXPORT Size ToCeiledSize(const SizeF& size); - -// Returns a Size with each component from the input SizeF rounded. -GFX_EXPORT Size ToRoundedSize(const SizeF& size); - -} // namespace gfx - -#endif // UI_GFX_SIZE_CONVERSIONS_H_ diff --git a/chromium/ui/gfx/size_f.h b/chromium/ui/gfx/size_f.h index b9065273c8e..0aabbcfcd53 100644 --- a/chromium/ui/gfx/size_f.h +++ b/chromium/ui/gfx/size_f.h @@ -1,54 +1,7 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_GFX_SIZE_F_H_ -#define UI_GFX_SIZE_F_H_ +// TODO(beng): remove once callsites are patched. +#include "ui/gfx/geometry/size_f.h" -#include <string> - -#include "base/compiler_specific.h" -#include "ui/gfx/gfx_export.h" -#include "ui/gfx/size_base.h" - -namespace gfx { - -// A floating version of gfx::Size. -class GFX_EXPORT SizeF : public SizeBase<SizeF, float> { - public: - SizeF() : SizeBase<SizeF, float>(0, 0) {} - SizeF(float width, float height) : SizeBase<SizeF, float>(width, height) {} - ~SizeF() {} - - void Scale(float scale) { - Scale(scale, scale); - } - - void Scale(float x_scale, float y_scale) { - SetSize(width() * x_scale, height() * y_scale); - } - - std::string ToString() const; -}; - -inline bool operator==(const SizeF& lhs, const SizeF& rhs) { - return lhs.width() == rhs.width() && lhs.height() == rhs.height(); -} - -inline bool operator!=(const SizeF& lhs, const SizeF& rhs) { - return !(lhs == rhs); -} - -GFX_EXPORT SizeF ScaleSize(const SizeF& p, float x_scale, float y_scale); - -inline SizeF ScaleSize(const SizeF& p, float scale) { - return ScaleSize(p, scale, scale); -} - -#if !defined(COMPILER_MSVC) -extern template class SizeBase<SizeF, float>; -#endif - -} // namespace gfx - -#endif // UI_GFX_SIZE_F_H_ diff --git a/chromium/ui/gfx/skbitmap_operations.cc b/chromium/ui/gfx/skbitmap_operations.cc index c89153692a4..d3b48271bbe 100644 --- a/chromium/ui/gfx/skbitmap_operations.cc +++ b/chromium/ui/gfx/skbitmap_operations.cc @@ -21,14 +21,12 @@ // static SkBitmap SkBitmapOperations::CreateInvertedBitmap(const SkBitmap& image) { - DCHECK(image.config() == SkBitmap::kARGB_8888_Config); + DCHECK(image.colorType() == kPMColor_SkColorType); SkAutoLockPixels lock_image(image); SkBitmap inverted; - inverted.setConfig(SkBitmap::kARGB_8888_Config, image.width(), image.height(), - 0); - inverted.allocPixels(); + inverted.allocN32Pixels(image.width(), image.height()); inverted.eraseARGB(0, 0, 0, 0); for (int y = 0; y < image.height(); ++y) { @@ -51,15 +49,13 @@ SkBitmap SkBitmapOperations::CreateSuperimposedBitmap(const SkBitmap& first, DCHECK(first.width() == second.width()); DCHECK(first.height() == second.height()); DCHECK(first.bytesPerPixel() == second.bytesPerPixel()); - DCHECK(first.config() == SkBitmap::kARGB_8888_Config); + DCHECK(first.colorType() == kPMColor_SkColorType); SkAutoLockPixels lock_first(first); SkAutoLockPixels lock_second(second); SkBitmap superimposed; - superimposed.setConfig(SkBitmap::kARGB_8888_Config, - first.width(), first.height()); - superimposed.allocPixels(); + superimposed.allocN32Pixels(first.width(), first.height()); superimposed.eraseARGB(0, 0, 0, 0); SkCanvas canvas(superimposed); @@ -84,7 +80,7 @@ SkBitmap SkBitmapOperations::CreateBlendedBitmap(const SkBitmap& first, DCHECK(first.width() == second.width()); DCHECK(first.height() == second.height()); DCHECK(first.bytesPerPixel() == second.bytesPerPixel()); - DCHECK(first.config() == SkBitmap::kARGB_8888_Config); + DCHECK(first.colorType() == kPMColor_SkColorType); // Optimize for case where we won't need to blend anything. static const double alpha_min = 1.0 / 255; @@ -98,9 +94,7 @@ SkBitmap SkBitmapOperations::CreateBlendedBitmap(const SkBitmap& first, SkAutoLockPixels lock_second(second); SkBitmap blended; - blended.setConfig(SkBitmap::kARGB_8888_Config, first.width(), first.height(), - 0); - blended.allocPixels(); + blended.allocN32Pixels(first.width(), first.height()); blended.eraseARGB(0, 0, 0, 0); double first_alpha = 1 - alpha; @@ -136,12 +130,11 @@ SkBitmap SkBitmapOperations::CreateMaskedBitmap(const SkBitmap& rgb, DCHECK(rgb.width() == alpha.width()); DCHECK(rgb.height() == alpha.height()); DCHECK(rgb.bytesPerPixel() == alpha.bytesPerPixel()); - DCHECK(rgb.config() == SkBitmap::kARGB_8888_Config); - DCHECK(alpha.config() == SkBitmap::kARGB_8888_Config); + DCHECK(rgb.colorType() == kPMColor_SkColorType); + DCHECK(alpha.colorType() == kPMColor_SkColorType); SkBitmap masked; - masked.setConfig(SkBitmap::kARGB_8888_Config, rgb.width(), rgb.height(), 0); - masked.allocPixels(); + masked.allocN32Pixels(rgb.width(), rgb.height()); masked.eraseARGB(0, 0, 0, 0); SkAutoLockPixels lock_rgb(rgb); @@ -174,13 +167,11 @@ SkBitmap SkBitmapOperations::CreateMaskedBitmap(const SkBitmap& rgb, SkBitmap SkBitmapOperations::CreateButtonBackground(SkColor color, const SkBitmap& image, const SkBitmap& mask) { - DCHECK(image.config() == SkBitmap::kARGB_8888_Config); - DCHECK(mask.config() == SkBitmap::kARGB_8888_Config); + DCHECK(image.colorType() == kPMColor_SkColorType); + DCHECK(mask.colorType() == kPMColor_SkColorType); SkBitmap background; - background.setConfig( - SkBitmap::kARGB_8888_Config, mask.width(), mask.height(), 0); - background.allocPixels(); + background.allocN32Pixels(mask.width(), mask.height()); double bg_a = SkColorGetA(color); double bg_r = SkColorGetR(color); @@ -556,12 +547,10 @@ SkBitmap SkBitmapOperations::CreateHSLShiftedBitmap( HSLShift::kLineProcessors[H_op][S_op][L_op]; DCHECK(bitmap.empty() == false); - DCHECK(bitmap.config() == SkBitmap::kARGB_8888_Config); + DCHECK(bitmap.colorType() == kPMColor_SkColorType); SkBitmap shifted; - shifted.setConfig(SkBitmap::kARGB_8888_Config, bitmap.width(), - bitmap.height()); - shifted.allocPixels(); + shifted.allocN32Pixels(bitmap.width(), bitmap.height()); shifted.eraseARGB(0, 0, 0, 0); SkAutoLockPixels lock_bitmap(bitmap); @@ -582,11 +571,10 @@ SkBitmap SkBitmapOperations::CreateHSLShiftedBitmap( SkBitmap SkBitmapOperations::CreateTiledBitmap(const SkBitmap& source, int src_x, int src_y, int dst_w, int dst_h) { - DCHECK(source.config() == SkBitmap::kARGB_8888_Config); + DCHECK(source.colorType() == kPMColor_SkColorType); SkBitmap cropped; - cropped.setConfig(SkBitmap::kARGB_8888_Config, dst_w, dst_h, 0); - cropped.allocPixels(); + cropped.allocN32Pixels(dst_w, dst_h); cropped.eraseARGB(0, 0, 0, 0); SkAutoLockPixels lock_source(source); @@ -635,9 +623,7 @@ SkBitmap SkBitmapOperations::DownsampleByTwo(const SkBitmap& bitmap) { return bitmap; SkBitmap result; - result.setConfig(SkBitmap::kARGB_8888_Config, - (bitmap.width() + 1) / 2, (bitmap.height() + 1) / 2); - result.allocPixels(); + result.allocN32Pixels((bitmap.width() + 1) / 2, (bitmap.height() + 1) / 2); SkAutoLockPixels lock(bitmap); @@ -702,10 +688,10 @@ SkBitmap SkBitmapOperations::UnPreMultiply(const SkBitmap& bitmap) { if (bitmap.isOpaque()) return bitmap; + SkImageInfo info = bitmap.info(); + info.fAlphaType = kOpaque_SkAlphaType; SkBitmap opaque_bitmap; - opaque_bitmap.setConfig(bitmap.config(), bitmap.width(), bitmap.height(), - 0, kOpaque_SkAlphaType); - opaque_bitmap.allocPixels(); + opaque_bitmap.allocPixels(info); { SkAutoLockPixels bitmap_lock(bitmap); @@ -725,12 +711,10 @@ SkBitmap SkBitmapOperations::UnPreMultiply(const SkBitmap& bitmap) { // static SkBitmap SkBitmapOperations::CreateTransposedBitmap(const SkBitmap& image) { - DCHECK(image.config() == SkBitmap::kARGB_8888_Config); + DCHECK(image.colorType() == kPMColor_SkColorType); SkBitmap transposed; - transposed.setConfig( - SkBitmap::kARGB_8888_Config, image.height(), image.width(), 0); - transposed.allocPixels(); + transposed.allocN32Pixels(image.height(), image.width()); SkAutoLockPixels lock_image(image); SkAutoLockPixels lock_transposed(transposed); @@ -749,12 +733,10 @@ SkBitmap SkBitmapOperations::CreateTransposedBitmap(const SkBitmap& image) { // static SkBitmap SkBitmapOperations::CreateColorMask(const SkBitmap& bitmap, SkColor c) { - DCHECK(bitmap.config() == SkBitmap::kARGB_8888_Config); + DCHECK(bitmap.colorType() == kPMColor_SkColorType); SkBitmap color_mask; - color_mask.setConfig(SkBitmap::kARGB_8888_Config, - bitmap.width(), bitmap.height()); - color_mask.allocPixels(); + color_mask.allocN32Pixels(bitmap.width(), bitmap.height()); color_mask.eraseARGB(0, 0, 0, 0); SkCanvas canvas(color_mask); @@ -771,7 +753,7 @@ SkBitmap SkBitmapOperations::CreateColorMask(const SkBitmap& bitmap, SkBitmap SkBitmapOperations::CreateDropShadow( const SkBitmap& bitmap, const gfx::ShadowValues& shadows) { - DCHECK(bitmap.config() == SkBitmap::kARGB_8888_Config); + DCHECK(bitmap.colorType() == kPMColor_SkColorType); // Shadow margin insets are negative values because they grow outside. // Negate them here as grow direction is not important and only pixel value @@ -779,10 +761,8 @@ SkBitmap SkBitmapOperations::CreateDropShadow( gfx::Insets shadow_margin = -gfx::ShadowValue::GetMargin(shadows); SkBitmap image_with_shadow; - image_with_shadow.setConfig(SkBitmap::kARGB_8888_Config, - bitmap.width() + shadow_margin.width(), - bitmap.height() + shadow_margin.height()); - image_with_shadow.allocPixels(); + image_with_shadow.allocN32Pixels(bitmap.width() + shadow_margin.width(), + bitmap.height() + shadow_margin.height()); image_with_shadow.eraseARGB(0, 0, 0, 0); SkCanvas canvas(image_with_shadow); @@ -796,8 +776,8 @@ SkBitmap SkBitmapOperations::CreateDropShadow( shadow.color()); skia::RefPtr<SkBlurImageFilter> filter = - skia::AdoptRef(new SkBlurImageFilter(SkDoubleToScalar(shadow.blur()), - SkDoubleToScalar(shadow.blur()))); + skia::AdoptRef(SkBlurImageFilter::Create( + SkDoubleToScalar(shadow.blur()), SkDoubleToScalar(shadow.blur()))); paint.setImageFilter(filter.get()); canvas.saveLayer(0, &paint); diff --git a/chromium/ui/gfx/skia_util.cc b/chromium/ui/gfx/skia_util.cc index bb7cde7b4ba..0a991df20aa 100644 --- a/chromium/ui/gfx/skia_util.cc +++ b/chromium/ui/gfx/skia_util.cc @@ -64,13 +64,16 @@ void TransformToFlattenedSkMatrix(const gfx::Transform& transform, skia::RefPtr<SkShader> CreateImageRepShader(const gfx::ImageSkiaRep& image_rep, SkShader::TileMode tile_mode, const SkMatrix& local_matrix) { - skia::RefPtr<SkShader> shader = skia::AdoptRef(SkShader::CreateBitmapShader( - image_rep.sk_bitmap(), tile_mode, tile_mode)); - SkScalar scale_x = local_matrix.getScaleX(); - SkScalar scale_y = local_matrix.getScaleY(); - SkScalar bitmap_scale = SkFloatToScalar(image_rep.scale()); + return CreateImageRepShaderForScale(image_rep, tile_mode, local_matrix, + image_rep.scale()); +} - // Unscale matrix by |bitmap_scale| such that the bitmap is drawn at the +skia::RefPtr<SkShader> CreateImageRepShaderForScale( + const gfx::ImageSkiaRep& image_rep, + SkShader::TileMode tile_mode, + const SkMatrix& local_matrix, + SkScalar scale) { + // Unscale matrix by |scale| such that the bitmap is drawn at the // correct density. // Convert skew and translation to pixel coordinates. // Thus, for |bitmap_scale| = 2: @@ -78,11 +81,12 @@ skia::RefPtr<SkShader> CreateImageRepShader(const gfx::ImageSkiaRep& image_rep, // should be converted to // x scale = 1, x translation = 2 pixels. SkMatrix shader_scale = local_matrix; - shader_scale.preScale(bitmap_scale, bitmap_scale); - shader_scale.setScaleX(SkScalarDiv(scale_x, bitmap_scale)); - shader_scale.setScaleY(SkScalarDiv(scale_y, bitmap_scale)); + shader_scale.preScale(scale, scale); + shader_scale.setScaleX(local_matrix.getScaleX() / scale); + shader_scale.setScaleY(local_matrix.getScaleY() / scale); - shader->setLocalMatrix(shader_scale); + skia::RefPtr<SkShader> shader = skia::AdoptRef(SkShader::CreateBitmapShader( + image_rep.sk_bitmap(), tile_mode, tile_mode, &shader_scale)); return shader; } @@ -99,15 +103,20 @@ skia::RefPtr<SkShader> CreateGradientShader(int start_point, grad_points, grad_colors, NULL, 2, SkShader::kRepeat_TileMode)); } +static SkScalar RadiusToSigma(double radius) { + // This captures historically what skia did under the hood. Now skia accepts + // sigma, not radius, so we perform the conversion. + return radius > 0 ? SkDoubleToScalar(0.57735f * radius + 0.5) : 0; +} + skia::RefPtr<SkDrawLooper> CreateShadowDrawLooper( const std::vector<ShadowValue>& shadows) { if (shadows.empty()) return skia::RefPtr<SkDrawLooper>(); - skia::RefPtr<SkLayerDrawLooper> looper = - skia::AdoptRef(new SkLayerDrawLooper); + SkLayerDrawLooper::Builder looper_builder; - looper->addLayer(); // top layer of the original. + looper_builder.addLayer(); // top layer of the original. SkLayerDrawLooper::LayerInfo layer_info; layer_info.fPaintBits |= SkLayerDrawLooper::kMaskFilter_Bit; @@ -123,19 +132,19 @@ skia::RefPtr<SkDrawLooper> CreateShadowDrawLooper( // SkBlurMaskFilter's blur radius defines the range to extend the blur from // original mask, which is half of blur amount as defined in ShadowValue. skia::RefPtr<SkMaskFilter> blur_mask = skia::AdoptRef( - SkBlurMaskFilter::Create(SkDoubleToScalar(shadow.blur() / 2), - SkBlurMaskFilter::kNormal_BlurStyle, + SkBlurMaskFilter::Create(kNormal_SkBlurStyle, + RadiusToSigma(shadow.blur() / 2), SkBlurMaskFilter::kHighQuality_BlurFlag)); skia::RefPtr<SkColorFilter> color_filter = skia::AdoptRef( SkColorFilter::CreateModeFilter(shadow.color(), SkXfermode::kSrcIn_Mode)); - SkPaint* paint = looper->addLayer(layer_info); + SkPaint* paint = looper_builder.addLayer(layer_info); paint->setMaskFilter(blur_mask.get()); paint->setColorFilter(color_filter.get()); } - return looper; + return skia::AdoptRef<SkDrawLooper>(looper_builder.detachLooper()); } bool BitmapsAreEqual(const SkBitmap& bitmap1, const SkBitmap& bitmap2) { diff --git a/chromium/ui/gfx/skia_util.h b/chromium/ui/gfx/skia_util.h index 92c886a4309..0c759838b86 100644 --- a/chromium/ui/gfx/skia_util.h +++ b/chromium/ui/gfx/skia_util.h @@ -47,6 +47,13 @@ GFX_EXPORT skia::RefPtr<SkShader> CreateImageRepShader( SkShader::TileMode tile_mode, const SkMatrix& local_matrix); +// Creates a bitmap shader for the image rep with the passed in scale factor. +GFX_EXPORT skia::RefPtr<SkShader> CreateImageRepShaderForScale( + const gfx::ImageSkiaRep& image_rep, + SkShader::TileMode tile_mode, + const SkMatrix& local_matrix, + SkScalar scale); + // Creates a vertical gradient shader. The caller owns the shader. // Example usage to avoid leaks: GFX_EXPORT skia::RefPtr<SkShader> CreateGradientShader(int start_point, diff --git a/chromium/ui/gfx/skia_utils_gtk.cc b/chromium/ui/gfx/skia_utils_gtk.cc deleted file mode 100644 index f7f3a0aaf2b..00000000000 --- a/chromium/ui/gfx/skia_utils_gtk.cc +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/gfx/skia_utils_gtk.h" - -#include <gdk/gdk.h> - -namespace gfx { - -const int kSkiaToGDKMultiplier = 257; - -// GDK_COLOR_RGB multiplies by 257 (= 0x10001) to distribute the bits evenly -// See: http://www.mindcontrol.org/~hplus/graphics/expand-bits.html -// To get back, we can just right shift by eight -// (or, formulated differently, i == (i*257)/256 for all i < 256). - -SkColor GdkColorToSkColor(GdkColor color) { - return SkColorSetRGB(color.red >> 8, color.green >> 8, color.blue >> 8); -} - -GdkColor SkColorToGdkColor(SkColor color) { - GdkColor gdk_color = { - 0, - static_cast<guint16>(SkColorGetR(color) * kSkiaToGDKMultiplier), - static_cast<guint16>(SkColorGetG(color) * kSkiaToGDKMultiplier), - static_cast<guint16>(SkColorGetB(color) * kSkiaToGDKMultiplier) - }; - return gdk_color; -} - -} // namespace gfx diff --git a/chromium/ui/gfx/skia_utils_gtk.h b/chromium/ui/gfx/skia_utils_gtk.h deleted file mode 100644 index 6b74da246ea..00000000000 --- a/chromium/ui/gfx/skia_utils_gtk.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_GFX_SKIA_UTILS_GTK_H_ -#define UI_GFX_SKIA_UTILS_GTK_H_ - -#include "third_party/skia/include/core/SkColor.h" -#include "ui/gfx/gfx_export.h" - -typedef struct _GdkColor GdkColor; - -namespace gfx { - -// Converts GdkColors to the ARGB layout Skia expects. -GFX_EXPORT SkColor GdkColorToSkColor(GdkColor color); - -// Converts ARGB to GdkColor. -GFX_EXPORT GdkColor SkColorToGdkColor(SkColor color); - -} // namespace gfx - -#endif // UI_GFX_SKIA_UTILS_GTK_H_ diff --git a/chromium/ui/gfx/skrect_conversion_unittest.cc b/chromium/ui/gfx/skrect_conversion_unittest.cc new file mode 100644 index 00000000000..ef116c2a451 --- /dev/null +++ b/chromium/ui/gfx/skrect_conversion_unittest.cc @@ -0,0 +1,26 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/basictypes.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/skia_util.h" + +namespace gfx { + +TEST(RectTest, SkiaRectConversions) { + Rect isrc(10, 20, 30, 40); + RectF fsrc(10.5f, 20.5f, 30.5f, 40.5f); + + SkIRect skirect = RectToSkIRect(isrc); + EXPECT_EQ(isrc.ToString(), SkIRectToRect(skirect).ToString()); + + SkRect skrect = RectToSkRect(isrc); + EXPECT_EQ(gfx::RectF(isrc).ToString(), SkRectToRectF(skrect).ToString()); + + skrect = RectFToSkRect(fsrc); + EXPECT_EQ(fsrc.ToString(), SkRectToRectF(skrect).ToString()); +} + +} // namespace gfx diff --git a/chromium/ui/gfx/switches.cc b/chromium/ui/gfx/switches.cc index 2f2eb9a3b82..2441a1483f5 100644 --- a/chromium/ui/gfx/switches.cc +++ b/chromium/ui/gfx/switches.cc @@ -6,11 +6,21 @@ namespace switches { +// The ImageSkia looks up the resource pack with the closest available scale +// factor instead of the actual device scale factor and then rescale on +// ImageSkia side. This switch disables this feature. +const char kDisableArbitraryScaleFactorInImageSkia[] = + "disable-arbitrary-scale-factor-in-image-skia"; + // Let text glyphs have X-positions that aren't snapped to the pixel grid in // the browser UI. const char kEnableBrowserTextSubpixelPositioning[] = "enable-browser-text-subpixel-positioning"; +// Uses the HarfBuzz port of RenderText on all platforms. +const char kEnableHarfBuzzRenderText[] = + "enable-harfbuzz-rendertext"; + // Enable text glyphs to have X-positions that aren't snapped to the pixel grid // in webkit renderers. const char kEnableWebkitTextSubpixelPositioning[] = @@ -19,7 +29,4 @@ const char kEnableWebkitTextSubpixelPositioning[] = // Overrides the device scale factor for the browser UI and the contents. const char kForceDeviceScaleFactor[] = "force-device-scale-factor"; -// Enables/Disables High DPI support (windows) -const char kHighDPISupport[] = "high-dpi-support"; - } // namespace switches diff --git a/chromium/ui/gfx/switches.h b/chromium/ui/gfx/switches.h index c5629f249c4..1b6e10b7560 100644 --- a/chromium/ui/gfx/switches.h +++ b/chromium/ui/gfx/switches.h @@ -9,10 +9,12 @@ namespace switches { +GFX_EXPORT extern const char kAllowArbitraryScaleFactorInImageSkia[]; +GFX_EXPORT extern const char kDisableArbitraryScaleFactorInImageSkia[]; GFX_EXPORT extern const char kEnableBrowserTextSubpixelPositioning[]; +GFX_EXPORT extern const char kEnableHarfBuzzRenderText[]; GFX_EXPORT extern const char kEnableWebkitTextSubpixelPositioning[]; GFX_EXPORT extern const char kForceDeviceScaleFactor[]; -GFX_EXPORT extern const char kHighDPISupport[]; } // namespace switches diff --git a/chromium/ui/gfx/sys_color_change_listener.cc b/chromium/ui/gfx/sys_color_change_listener.cc index d5646b3a81c..4a394aea5ee 100644 --- a/chromium/ui/gfx/sys_color_change_listener.cc +++ b/chromium/ui/gfx/sys_color_change_listener.cc @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ui/gfx/color_utils.h" #include "ui/gfx/sys_color_change_listener.h" #if defined(OS_WIN) @@ -12,6 +11,8 @@ #include "base/basictypes.h" #include "base/memory/singleton.h" #include "base/observer_list.h" +#include "ui/gfx/color_utils.h" + #if defined(OS_WIN) #include "ui/gfx/win/singleton_hwnd.h" #endif diff --git a/chromium/ui/gfx/text_constants.h b/chromium/ui/gfx/text_constants.h index 4ac788e6109..47c91cf5f14 100644 --- a/chromium/ui/gfx/text_constants.h +++ b/chromium/ui/gfx/text_constants.h @@ -10,34 +10,25 @@ namespace gfx { // TODO(msw): Distinguish between logical character stops and glyph stops? // TODO(msw): Merge with base::i18n::BreakIterator::BreakType. enum BreakType { - // Stop cursor movement on neighboring characters. - CHARACTER_BREAK = 0, - // Stop cursor movement on nearest word boundaries. - WORD_BREAK, - // Stop cursor movement on line ends as shown on screen. - LINE_BREAK, + CHARACTER_BREAK = 0, // Stop cursor movement on neighboring characters. + WORD_BREAK, // Stop cursor movement on nearest word boundaries. + LINE_BREAK, // Stop cursor movement on line ends as shown on screen. }; // Horizontal text alignment modes. enum HorizontalAlignment { - // Align the text's left edge with that of its display area. - ALIGN_LEFT = 0, - // Align the text's center with that of its display area. - ALIGN_CENTER, - // Align the text's right edge with that of its display area. - ALIGN_RIGHT, + ALIGN_LEFT = 0, // Align the text's left edge with that of its display area. + ALIGN_CENTER, // Align the text's center with that of its display area. + ALIGN_RIGHT, // Align the text's right edge with that of its display area. + ALIGN_TO_HEAD, // Align the text to its first strong character's direction. }; // The directionality modes used to determine the base text direction. enum DirectionalityMode { - // Use the first strong character's direction. - DIRECTIONALITY_FROM_TEXT = 0, - // Use the UI locale's text reading direction. - DIRECTIONALITY_FROM_UI, - // Use LTR regardless of content or UI locale. - DIRECTIONALITY_FORCE_LTR, - // Use RTL regardless of content or UI locale. - DIRECTIONALITY_FORCE_RTL, + DIRECTIONALITY_FROM_TEXT = 0, // Use the first strong character's direction. + DIRECTIONALITY_FROM_UI, // Use the UI locale's text reading direction. + DIRECTIONALITY_FORCE_LTR, // Use LTR regardless of content or UI locale. + DIRECTIONALITY_FORCE_RTL, // Use RTL regardless of content or UI locale. }; // Text styles and adornments. @@ -51,6 +42,16 @@ enum TextStyle { NUM_TEXT_STYLES, }; +// Elision behaviors of text that exceeds constrained dimensions. +enum ElideBehavior { + TRUNCATE = 0, // Do not elide or fade; the text may be truncated at the end. + ELIDE_HEAD, // Add an ellipsis at the start of the string. + ELIDE_MIDDLE, // Add an ellipsis in the middle of the string. + ELIDE_TAIL, // Add an ellipsis at the end of the string. + ELIDE_EMAIL, // Add ellipses to username and domain substrings. + FADE_TAIL, // Fade the string's end opposite of its horizontal alignment. +}; + } // namespace gfx #endif // UI_GFX_TEXT_CONSTANTS_H_ diff --git a/chromium/ui/gfx/text_elider.cc b/chromium/ui/gfx/text_elider.cc index 5b9469614f2..8c07bdecd5f 100644 --- a/chromium/ui/gfx/text_elider.cc +++ b/chromium/ui/gfx/text_elider.cc @@ -21,135 +21,29 @@ #include "base/strings/string_util.h" #include "base/strings/sys_string_conversions.h" #include "base/strings/utf_string_conversions.h" -#include "net/base/escape.h" -#include "net/base/net_util.h" -#include "net/base/registry_controlled_domains/registry_controlled_domain.h" #include "third_party/icu/source/common/unicode/rbbi.h" #include "third_party/icu/source/common/unicode/uloc.h" #include "ui/gfx/font_list.h" #include "ui/gfx/text_utils.h" -#include "url/gurl.h" -namespace gfx { +using base::ASCIIToUTF16; +using base::UTF8ToUTF16; +using base::WideToUTF16; -// U+2026 in utf8 -const char kEllipsis[] = "\xE2\x80\xA6"; -const base::char16 kEllipsisUTF16[] = { 0x2026, 0 }; -const base::char16 kForwardSlash = '/'; +namespace gfx { namespace { -// Helper class to split + elide text, while respecting UTF16 surrogate pairs. -class StringSlicer { - public: - StringSlicer(const base::string16& text, - const base::string16& ellipsis, - bool elide_in_middle) - : text_(text), - ellipsis_(ellipsis), - elide_in_middle_(elide_in_middle) { - } - - // Cuts |text_| to be |length| characters long. If |elide_in_middle_| is true, - // the middle of the string is removed to leave equal-length pieces from the - // beginning and end of the string; otherwise, the end of the string is - // removed and only the beginning remains. If |insert_ellipsis| is true, - // then an ellipsis character will be inserted at the cut point. - base::string16 CutString(size_t length, bool insert_ellipsis) { - const base::string16 ellipsis_text = insert_ellipsis ? ellipsis_ - : base::string16(); - - if (!elide_in_middle_) - return text_.substr(0, FindValidBoundaryBefore(length)) + ellipsis_text; - - // We put the extra character, if any, before the cut. - const size_t half_length = length / 2; - const size_t prefix_length = FindValidBoundaryBefore(length - half_length); - const size_t suffix_start_guess = text_.length() - half_length; - const size_t suffix_start = FindValidBoundaryAfter(suffix_start_guess); - const size_t suffix_length = - half_length - (suffix_start_guess - suffix_start); - return text_.substr(0, prefix_length) + ellipsis_text + - text_.substr(suffix_start, suffix_length); - } - - private: - // Returns a valid cut boundary at or before |index|. - size_t FindValidBoundaryBefore(size_t index) const { - DCHECK_LE(index, text_.length()); - if (index != text_.length()) - U16_SET_CP_START(text_.data(), 0, index); - return index; - } - - // Returns a valid cut boundary at or after |index|. - size_t FindValidBoundaryAfter(size_t index) const { - DCHECK_LE(index, text_.length()); - if (index != text_.length()) - U16_SET_CP_LIMIT(text_.data(), 0, index, text_.length()); - return index; - } - - // The text to be sliced. - const base::string16& text_; - - // Ellipsis string to use. - const base::string16& ellipsis_; - - // If true, the middle of the string will be elided. - bool elide_in_middle_; - - DISALLOW_COPY_AND_ASSIGN(StringSlicer); -}; - -// Build a path from the first |num_components| elements in |path_elements|. -// Prepends |path_prefix|, appends |filename|, inserts ellipsis if appropriate. -base::string16 BuildPathFromComponents( - const base::string16& path_prefix, - const std::vector<base::string16>& path_elements, - const base::string16& filename, - size_t num_components) { - // Add the initial elements of the path. - base::string16 path = path_prefix; - - // Build path from first |num_components| elements. - for (size_t j = 0; j < num_components; ++j) - path += path_elements[j] + kForwardSlash; - - // Add |filename|, ellipsis if necessary. - if (num_components != (path_elements.size() - 1)) - path += base::string16(kEllipsisUTF16) + kForwardSlash; - path += filename; - - return path; -} - -// Takes a prefix (Domain, or Domain+subdomain) and a collection of path -// components and elides if possible. Returns a string containing the longest -// possible elided path, or an empty string if elision is not possible. -base::string16 ElideComponentizedPath( - const base::string16& url_path_prefix, - const std::vector<base::string16>& url_path_elements, - const base::string16& url_filename, - const base::string16& url_query, - const FontList& font_list, - float available_pixel_width) { - const size_t url_path_number_of_elements = url_path_elements.size(); - - CHECK(url_path_number_of_elements); - for (size_t i = url_path_number_of_elements - 1; i > 0; --i) { - base::string16 elided_path = BuildPathFromComponents(url_path_prefix, - url_path_elements, url_filename, i); - if (available_pixel_width >= GetStringWidthF(elided_path, font_list)) - return ElideText(elided_path + url_query, font_list, - available_pixel_width, ELIDE_AT_END); - } - - return base::string16(); -} - -} // namespace - +// Elides a well-formed email address (e.g. username@domain.com) to fit into +// |available_pixel_width| using the specified |font_list|. +// This function guarantees that the string returned will contain at least one +// character, other than the ellipses, on either side of the '@'. If it is +// impossible to achieve these requirements: only an ellipsis will be returned. +// If possible: this elides only the username portion of the |email|. Otherwise, +// the domain is elided in the middle so that it splits the available width +// equally with the elided username (should the username be short enough that it +// doesn't need half the available width: the elided domain will occupy that +// extra width). base::string16 ElideEmail(const base::string16& email, const FontList& font_list, float available_pixel_width) { @@ -191,8 +85,7 @@ base::string16 ElideEmail(const base::string16& email, std::min(available_domain_width, std::max(available_pixel_width - full_username_width, available_pixel_width / 2)); - domain = ElideText(domain, font_list, desired_domain_width, - ELIDE_IN_MIDDLE); + domain = ElideText(domain, font_list, desired_domain_width, ELIDE_MIDDLE); // Failing to elide the domain such that at least one character remains // (other than the ellipsis itself) remains: return a single ellipsis. if (domain.length() <= 1U) @@ -203,206 +96,61 @@ base::string16 ElideEmail(const base::string16& email, // is guaranteed to fit with at least one character remaining given all the // precautions taken earlier). available_pixel_width -= GetStringWidthF(domain, font_list); - username = ElideText(username, font_list, available_pixel_width, - ELIDE_AT_END); - + username = ElideText(username, font_list, available_pixel_width, ELIDE_TAIL); return username + kAtSignUTF16 + domain; } -// TODO(pkasting): http://crbug.com/77883 This whole function gets -// kerning/ligatures/etc. issues potentially wrong by assuming that the width of -// a rendered string is always the sum of the widths of its substrings. Also I -// suspect it could be made simpler. -base::string16 ElideUrl(const GURL& url, - const FontList& font_list, - float available_pixel_width, - const std::string& languages) { - // Get a formatted string and corresponding parsing of the url. - url_parse::Parsed parsed; - const base::string16 url_string = - net::FormatUrl(url, languages, net::kFormatUrlOmitAll, - net::UnescapeRule::SPACES, &parsed, NULL, NULL); - if (available_pixel_width <= 0) - return url_string; - - // If non-standard, return plain eliding. - if (!url.IsStandard()) - return ElideText(url_string, font_list, available_pixel_width, - ELIDE_AT_END); - - // Now start eliding url_string to fit within available pixel width. - // Fist pass - check to see whether entire url_string fits. - const float pixel_width_url_string = GetStringWidthF(url_string, font_list); - if (available_pixel_width >= pixel_width_url_string) - return url_string; - - // Get the path substring, including query and reference. - const size_t path_start_index = parsed.path.begin; - const size_t path_len = parsed.path.len; - base::string16 url_path_query_etc = url_string.substr(path_start_index); - base::string16 url_path = url_string.substr(path_start_index, path_len); - - // Return general elided text if url minus the query fits. - const base::string16 url_minus_query = - url_string.substr(0, path_start_index + path_len); - if (available_pixel_width >= GetStringWidthF(url_minus_query, font_list)) - return ElideText(url_string, font_list, available_pixel_width, - ELIDE_AT_END); - - // Get Host. - base::string16 url_host = UTF8ToUTF16(url.host()); - - // Get domain and registry information from the URL. - base::string16 url_domain = UTF8ToUTF16( - net::registry_controlled_domains::GetDomainAndRegistry( - url, net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES)); - if (url_domain.empty()) - url_domain = url_host; - - // Add port if required. - if (!url.port().empty()) { - url_host += UTF8ToUTF16(":" + url.port()); - url_domain += UTF8ToUTF16(":" + url.port()); - } - - // Get sub domain. - base::string16 url_subdomain; - const size_t domain_start_index = url_host.find(url_domain); - if (domain_start_index != base::string16::npos) - url_subdomain = url_host.substr(0, domain_start_index); - const base::string16 kWwwPrefix = UTF8ToUTF16("www."); - if ((url_subdomain == kWwwPrefix || url_subdomain.empty() || - url.SchemeIsFile())) { - url_subdomain.clear(); - } - - // If this is a file type, the path is now defined as everything after ":". - // For example, "C:/aa/aa/bb", the path is "/aa/bb/cc". Interesting, the - // domain is now C: - this is a nice hack for eliding to work pleasantly. - if (url.SchemeIsFile()) { - // Split the path string using ":" - std::vector<base::string16> file_path_split; - base::SplitString(url_path, ':', &file_path_split); - if (file_path_split.size() > 1) { // File is of type "file:///C:/.." - url_host.clear(); - url_domain.clear(); - url_subdomain.clear(); - - const base::string16 kColon = UTF8ToUTF16(":"); - url_host = url_domain = file_path_split.at(0).substr(1) + kColon; - url_path_query_etc = url_path = file_path_split.at(1); - } - } +} // namespace - // Second Pass - remove scheme - the rest fits. - const float pixel_width_url_host = GetStringWidthF(url_host, font_list); - const float pixel_width_url_path = GetStringWidthF(url_path_query_etc, - font_list); - if (available_pixel_width >= - pixel_width_url_host + pixel_width_url_path) - return url_host + url_path_query_etc; - - // Third Pass: Subdomain, domain and entire path fits. - const float pixel_width_url_domain = GetStringWidthF(url_domain, font_list); - const float pixel_width_url_subdomain = - GetStringWidthF(url_subdomain, font_list); - if (available_pixel_width >= - pixel_width_url_subdomain + pixel_width_url_domain + - pixel_width_url_path) - return url_subdomain + url_domain + url_path_query_etc; - - // Query element. - base::string16 url_query; - const float kPixelWidthDotsTrailer = GetStringWidthF( - base::string16(kEllipsisUTF16), font_list); - if (parsed.query.is_nonempty()) { - url_query = UTF8ToUTF16("?") + url_string.substr(parsed.query.begin); - if (available_pixel_width >= - (pixel_width_url_subdomain + pixel_width_url_domain + - pixel_width_url_path - GetStringWidthF(url_query, font_list))) { - return ElideText(url_subdomain + url_domain + url_path_query_etc, - font_list, available_pixel_width, ELIDE_AT_END); - } - } +// U+2026 in utf8 +const char kEllipsis[] = "\xE2\x80\xA6"; +const base::char16 kEllipsisUTF16[] = { 0x2026, 0 }; +const base::char16 kForwardSlash = '/'; - // Parse url_path using '/'. - std::vector<base::string16> url_path_elements; - base::SplitString(url_path, kForwardSlash, &url_path_elements); - - // Get filename - note that for a path ending with / - // such as www.google.com/intl/ads/, the file name is ads/. - size_t url_path_number_of_elements = url_path_elements.size(); - DCHECK(url_path_number_of_elements != 0); - base::string16 url_filename; - if ((url_path_elements.at(url_path_number_of_elements - 1)).length() > 0) { - url_filename = *(url_path_elements.end() - 1); - } else if (url_path_number_of_elements > 1) { // Path ends with a '/'. - url_filename = url_path_elements.at(url_path_number_of_elements - 2) + - kForwardSlash; - url_path_number_of_elements--; - } - DCHECK(url_path_number_of_elements != 0); - - const size_t kMaxNumberOfUrlPathElementsAllowed = 1024; - if (url_path_number_of_elements <= 1 || - url_path_number_of_elements > kMaxNumberOfUrlPathElementsAllowed) { - // No path to elide, or too long of a path (could overflow in loop below) - // Just elide this as a text string. - return ElideText(url_subdomain + url_domain + url_path_query_etc, font_list, - available_pixel_width, ELIDE_AT_END); - } +StringSlicer::StringSlicer(const base::string16& text, + const base::string16& ellipsis, + bool elide_in_middle, + bool elide_at_beginning) + : text_(text), + ellipsis_(ellipsis), + elide_in_middle_(elide_in_middle), + elide_at_beginning_(elide_at_beginning) { +} - // Start eliding the path and replacing elements by ".../". - const base::string16 kEllipsisAndSlash = - base::string16(kEllipsisUTF16) + kForwardSlash; - const float pixel_width_ellipsis_slash = - GetStringWidthF(kEllipsisAndSlash, font_list); - - // Check with both subdomain and domain. - base::string16 elided_path = - ElideComponentizedPath(url_subdomain + url_domain, url_path_elements, - url_filename, url_query, font_list, - available_pixel_width); - if (!elided_path.empty()) - return elided_path; - - // Check with only domain. - // If a subdomain is present, add an ellipsis before domain. - // This is added only if the subdomain pixel width is larger than - // the pixel width of kEllipsis. Otherwise, subdomain remains, - // which means that this case has been resolved earlier. - base::string16 url_elided_domain = url_subdomain + url_domain; - if (pixel_width_url_subdomain > kPixelWidthDotsTrailer) { - if (!url_subdomain.empty()) - url_elided_domain = kEllipsisAndSlash[0] + url_domain; - else - url_elided_domain = url_domain; - - elided_path = ElideComponentizedPath(url_elided_domain, url_path_elements, - url_filename, url_query, font_list, - available_pixel_width); - - if (!elided_path.empty()) - return elided_path; - } +base::string16 StringSlicer::CutString(size_t length, bool insert_ellipsis) { + const base::string16 ellipsis_text = insert_ellipsis ? ellipsis_ + : base::string16(); + + if (elide_at_beginning_) + return ellipsis_text + + text_.substr(FindValidBoundaryBefore(text_.length() - length)); + + if (!elide_in_middle_) + return text_.substr(0, FindValidBoundaryBefore(length)) + ellipsis_text; + + // We put the extra character, if any, before the cut. + const size_t half_length = length / 2; + const size_t prefix_length = FindValidBoundaryBefore(length - half_length); + const size_t suffix_start_guess = text_.length() - half_length; + const size_t suffix_start = FindValidBoundaryAfter(suffix_start_guess); + const size_t suffix_length = + half_length - (suffix_start_guess - suffix_start); + return text_.substr(0, prefix_length) + ellipsis_text + + text_.substr(suffix_start, suffix_length); +} - // Return elided domain/.../filename anyway. - base::string16 final_elided_url_string(url_elided_domain); - const float url_elided_domain_width = GetStringWidthF(url_elided_domain, - font_list); - - // A hack to prevent trailing ".../...". - if ((available_pixel_width - url_elided_domain_width) > - pixel_width_ellipsis_slash + kPixelWidthDotsTrailer + - GetStringWidthF(ASCIIToUTF16("UV"), font_list)) { - final_elided_url_string += BuildPathFromComponents(base::string16(), - url_path_elements, url_filename, 1); - } else { - final_elided_url_string += url_path; - } +size_t StringSlicer::FindValidBoundaryBefore(size_t index) const { + DCHECK_LE(index, text_.length()); + if (index != text_.length()) + U16_SET_CP_START(text_.data(), 0, index); + return index; +} - return ElideText(final_elided_url_string, font_list, available_pixel_width, - ELIDE_AT_END); +size_t StringSlicer::FindValidBoundaryAfter(size_t index) const { + DCHECK_LE(index, text_.length()); + if (index != text_.length()) + U16_SET_CP_LIMIT(text_.data(), 0, index, text_.length()); + return index; } base::string16 ElideFilename(const base::FilePath& filename, @@ -426,8 +174,8 @@ base::string16 ElideFilename(const base::FilePath& filename, return base::i18n::GetDisplayStringInLTRDirectionality(filename_utf16); if (rootname.empty() || extension.empty()) { - const base::string16 elided_name = ElideText(filename_utf16, font_list, - available_pixel_width, ELIDE_AT_END); + const base::string16 elided_name = + ElideText(filename_utf16, font_list, available_pixel_width, ELIDE_TAIL); return base::i18n::GetDisplayStringInLTRDirectionality(elided_name); } @@ -442,14 +190,13 @@ base::string16 ElideFilename(const base::FilePath& filename, if (ext_width >= available_pixel_width) { const base::string16 elided_name = ElideText( - rootname + extension, font_list, available_pixel_width, - ELIDE_IN_MIDDLE); + rootname + extension, font_list, available_pixel_width, ELIDE_MIDDLE); return base::i18n::GetDisplayStringInLTRDirectionality(elided_name); } float available_root_width = available_pixel_width - ext_width; base::string16 elided_name = - ElideText(rootname, font_list, available_root_width, ELIDE_AT_END); + ElideText(rootname, font_list, available_root_width, ELIDE_TAIL); elided_name += extension; return base::i18n::GetDisplayStringInLTRDirectionality(elided_name); } @@ -457,16 +204,19 @@ base::string16 ElideFilename(const base::FilePath& filename, base::string16 ElideText(const base::string16& text, const FontList& font_list, float available_pixel_width, - ElideBehavior elide_behavior) { - if (text.empty()) + ElideBehavior behavior) { + DCHECK_NE(behavior, FADE_TAIL); + if (text.empty() || behavior == FADE_TAIL) return text; + if (behavior == ELIDE_EMAIL) + return ElideEmail(text, font_list, available_pixel_width); const float current_text_pixel_width = GetStringWidthF(text, font_list); - const bool elide_in_middle = (elide_behavior == ELIDE_IN_MIDDLE); - const bool insert_ellipsis = (elide_behavior != TRUNCATE_AT_END); - + const bool elide_in_middle = (behavior == ELIDE_MIDDLE); + const bool elide_at_beginning = (behavior == ELIDE_HEAD); + const bool insert_ellipsis = (behavior != TRUNCATE); const base::string16 ellipsis = base::string16(kEllipsisUTF16); - StringSlicer slicer(text, ellipsis, elide_in_middle); + StringSlicer slicer(text, ellipsis, elide_in_middle, elide_at_beginning); // Pango will return 0 width for absurdly long strings. Cut the string in // half and try again. @@ -476,9 +226,10 @@ base::string16 ElideText(const base::string16& text, // return positive numbers again. Detecting that is probably not worth it // (eliding way too much from a ridiculous string is probably still // ridiculous), but we should check other widths for bogus values as well. - if (current_text_pixel_width <= 0 && !text.empty()) { - const base::string16 cut = slicer.CutString(text.length() / 2, false); - return ElideText(cut, font_list, available_pixel_width, elide_behavior); + if (current_text_pixel_width <= 0) { + const base::string16 cut = + slicer.CutString(text.length() / 2, insert_ellipsis); + return ElideText(cut, font_list, available_pixel_width, behavior); } if (current_text_pixel_width <= available_pixel_width) @@ -493,103 +244,30 @@ base::string16 ElideText(const base::string16& text, size_t hi = text.length() - 1; size_t guess; for (guess = (lo + hi) / 2; lo <= hi; guess = (lo + hi) / 2) { - // We check the length of the whole desired string at once to ensure we + // We check the width of the whole desired string at once to ensure we // handle kerning/ligatures/etc. correctly. + // TODO(skanuj) : Handle directionality of ellipsis based on adjacent + // characters. See crbug.com/327963. const base::string16 cut = slicer.CutString(guess, insert_ellipsis); - const float guess_length = GetStringWidthF(cut, font_list); - // Check again that we didn't hit a Pango width overflow. If so, cut the - // current string in half and start over. - if (guess_length <= 0) { - return ElideText(slicer.CutString(guess / 2, false), - font_list, available_pixel_width, elide_behavior); - } - if (guess_length > available_pixel_width) + const float guess_width = GetStringWidthF(cut, font_list); + if (guess_width == available_pixel_width) + break; + if (guess_width > available_pixel_width) { hi = guess - 1; - else + // Move back if we are on loop terminating condition, and guess is wider + // than available. + if (hi < lo) + lo = hi; + } else { lo = guess + 1; + } } return slicer.CutString(guess, insert_ellipsis); } -base::string16 ElideText(const base::string16& text, - const Font& font, - float available_pixel_width, - ElideBehavior elide_behavior) { - return ElideText(text, FontList(font), available_pixel_width, elide_behavior); -} - -SortedDisplayURL::SortedDisplayURL(const GURL& url, - const std::string& languages) { - net::AppendFormattedHost(url, languages, &sort_host_); - base::string16 host_minus_www = net::StripWWW(sort_host_); - url_parse::Parsed parsed; - display_url_ = - net::FormatUrl(url, languages, net::kFormatUrlOmitAll, - net::UnescapeRule::SPACES, &parsed, &prefix_end_, NULL); - if (sort_host_.length() > host_minus_www.length()) { - prefix_end_ += sort_host_.length() - host_minus_www.length(); - sort_host_.swap(host_minus_www); - } -} - -SortedDisplayURL::SortedDisplayURL() : prefix_end_(0) { -} - -SortedDisplayURL::~SortedDisplayURL() { -} - -int SortedDisplayURL::Compare(const SortedDisplayURL& other, - icu::Collator* collator) const { - // Compare on hosts first. The host won't contain 'www.'. - UErrorCode compare_status = U_ZERO_ERROR; - UCollationResult host_compare_result = collator->compare( - static_cast<const UChar*>(sort_host_.c_str()), - static_cast<int>(sort_host_.length()), - static_cast<const UChar*>(other.sort_host_.c_str()), - static_cast<int>(other.sort_host_.length()), - compare_status); - DCHECK(U_SUCCESS(compare_status)); - if (host_compare_result != 0) - return host_compare_result; - - // Hosts match, compare on the portion of the url after the host. - base::string16 path = this->AfterHost(); - base::string16 o_path = other.AfterHost(); - compare_status = U_ZERO_ERROR; - UCollationResult path_compare_result = collator->compare( - static_cast<const UChar*>(path.c_str()), - static_cast<int>(path.length()), - static_cast<const UChar*>(o_path.c_str()), - static_cast<int>(o_path.length()), - compare_status); - DCHECK(U_SUCCESS(compare_status)); - if (path_compare_result != 0) - return path_compare_result; - - // Hosts and paths match, compare on the complete url. This'll push the www. - // ones to the end. - compare_status = U_ZERO_ERROR; - UCollationResult display_url_compare_result = collator->compare( - static_cast<const UChar*>(display_url_.c_str()), - static_cast<int>(display_url_.length()), - static_cast<const UChar*>(other.display_url_.c_str()), - static_cast<int>(other.display_url_.length()), - compare_status); - DCHECK(U_SUCCESS(compare_status)); - return display_url_compare_result; -} - -base::string16 SortedDisplayURL::AfterHost() const { - const size_t slash_index = display_url_.find(sort_host_, prefix_end_); - if (slash_index == base::string16::npos) { - NOTREACHED(); - return base::string16(); - } - return display_url_.substr(slash_index + sort_host_.length()); -} - -bool ElideString(const base::string16& input, int max_len, +bool ElideString(const base::string16& input, + int max_len, base::string16* output) { DCHECK_GE(max_len, 0); if (static_cast<int>(input.length()) <= max_len) { @@ -915,7 +593,7 @@ int RectangleText::Finalize() { // Remove trailing whitespace from the last line or remove the last line // completely, if it's just whitespace. if (!insufficient_height_ && !lines_->empty()) { - TrimWhitespace(lines_->back(), TRIM_TRAILING, &lines_->back()); + base::TrimWhitespace(lines_->back(), base::TRIM_TRAILING, &lines_->back()); if (lines_->back().empty() && !last_line_ended_in_lf_) lines_->pop_back(); } @@ -943,9 +621,10 @@ void RectangleText::AddLine(const base::string16& line) { if (truncate) { // Trim trailing whitespace from the line that was added. const int line = lines_->size() - lines_added; - TrimWhitespace(lines_->at(line), TRIM_TRAILING, &lines_->at(line)); + base::TrimWhitespace(lines_->at(line), base::TRIM_TRAILING, + &lines_->at(line)); } - if (ContainsOnlyWhitespace(word)) { + if (base::ContainsOnlyChars(word, base::kWhitespaceUTF16)) { // Skip the first space if the previous line was carried over. current_width_ = 0; current_line_.clear(); @@ -967,11 +646,10 @@ int RectangleText::WrapWord(const base::string16& word) { bool first_fragment = true; while (!insufficient_height_ && !text.empty()) { base::string16 fragment = - ElideText(text, font_list_, available_pixel_width_, - TRUNCATE_AT_END); + ElideText(text, font_list_, available_pixel_width_, TRUNCATE); // At least one character has to be added at every line, even if the // available space is too small. - if(fragment.empty()) + if (fragment.empty()) fragment = text.substr(0, 1); if (!first_fragment && NewLine()) lines_added++; @@ -999,7 +677,7 @@ int RectangleText::AddWordOverflow(const base::string16& word) { lines_added += WrapWord(word); } else { const ElideBehavior elide_behavior = - (wrap_behavior_ == ELIDE_LONG_WORDS ? ELIDE_AT_END : TRUNCATE_AT_END); + (wrap_behavior_ == ELIDE_LONG_WORDS ? ELIDE_TAIL : TRUNCATE); const base::string16 elided_word = ElideText(word, font_list_, available_pixel_width_, elide_behavior); AddToCurrentLine(elided_word); @@ -1012,7 +690,7 @@ int RectangleText::AddWordOverflow(const base::string16& word) { int RectangleText::AddWord(const base::string16& word) { int lines_added = 0; base::string16 trimmed; - TrimWhitespace(word, TRIM_TRAILING, &trimmed); + base::TrimWhitespace(word, base::TRIM_TRAILING, &trimmed); const float trimmed_width = GetStringWidthF(trimmed, font_list_); if (trimmed_width <= available_pixel_width_) { // Word can be made to fit, no need to fragment it. diff --git a/chromium/ui/gfx/text_elider.h b/chromium/ui/gfx/text_elider.h index 4eaca828c7d..a416fda779e 100644 --- a/chromium/ui/gfx/text_elider.h +++ b/chromium/ui/gfx/text_elider.h @@ -12,9 +12,8 @@ #include "base/basictypes.h" #include "base/strings/string16.h" -#include "third_party/icu/source/common/unicode/uchar.h" -#include "third_party/icu/source/i18n/unicode/coll.h" #include "ui/gfx/gfx_export.h" +#include "ui/gfx/text_constants.h" class GURL; @@ -23,65 +22,52 @@ class FilePath; } namespace gfx { -class Font; class FontList; GFX_EXPORT extern const char kEllipsis[]; GFX_EXPORT extern const base::char16 kEllipsisUTF16[]; +GFX_EXPORT extern const base::char16 kForwardSlash; -// Elides a well-formed email address (e.g. username@domain.com) to fit into -// |available_pixel_width| using the specified |font_list|. -// This function guarantees that the string returned will contain at least one -// character, other than the ellipses, on either side of the '@'. If it is -// impossible to achieve these requirements: only an ellipsis will be returned. -// If possible: this elides only the username portion of the |email|. Otherwise, -// the domain is elided in the middle so that it splits the available width -// equally with the elided username (should the username be short enough that it -// doesn't need half the available width: the elided domain will occupy that -// extra width). -GFX_EXPORT base::string16 ElideEmail(const base::string16& email, - const gfx::FontList& font_list, - float available_pixel_width); - -// This function takes a GURL object and elides it. It returns a string -// which composed of parts from subdomain, domain, path, filename and query. -// A "..." is added automatically at the end if the elided string is bigger -// than the |available_pixel_width|. For |available_pixel_width| == 0, a -// formatted, but un-elided, string is returned. |languages| is a comma -// separated list of ISO 639 language codes and is used to determine what -// characters are understood by a user. It should come from -// |prefs::kAcceptLanguages|. -// -// Note: in RTL locales, if the URL returned by this function is going to be -// displayed in the UI, then it is likely that the string needs to be marked -// as an LTR string (using base::i18n::WrapStringWithLTRFormatting()) so that it -// is displayed properly in an RTL context. Please refer to -// http://crbug.com/6487 for more information. -GFX_EXPORT base::string16 ElideUrl(const GURL& url, - const gfx::FontList& font_list, - float available_pixel_width, - const std::string& languages); - -enum ElideBehavior { - // Add ellipsis at the end of the string. - ELIDE_AT_END, - // Add ellipsis in the middle of the string. - ELIDE_IN_MIDDLE, - // Truncate the end of the string. - TRUNCATE_AT_END +// Helper class to split + elide text, while respecting UTF16 surrogate pairs. +class StringSlicer { + public: + StringSlicer(const base::string16& text, + const base::string16& ellipsis, + bool elide_in_middle, + bool elide_at_beginning); + + // Cuts |text_| to be |length| characters long. If |elide_in_middle_| is true, + // the middle of the string is removed to leave equal-length pieces from the + // beginning and end of the string; otherwise, the end of the string is + // removed and only the beginning remains. If |insert_ellipsis| is true, + // then an ellipsis character will be inserted at the cut point. + base::string16 CutString(size_t length, bool insert_ellipsis); + + private: + // Returns a valid cut boundary at or before/after |index|. + size_t FindValidBoundaryBefore(size_t index) const; + size_t FindValidBoundaryAfter(size_t index) const; + + // The text to be sliced. + const base::string16& text_; + + // Ellipsis string to use. + const base::string16& ellipsis_; + + // If true, the middle of the string will be elided. + bool elide_in_middle_; + + // If true, the beginning of the string will be elided. + bool elide_at_beginning_; + + DISALLOW_COPY_AND_ASSIGN(StringSlicer); }; -// Elides |text| to fit in |available_pixel_width| according to the specified -// |elide_behavior|. +// Elides |text| to fit the |available_pixel_width| with the specified behavior. GFX_EXPORT base::string16 ElideText(const base::string16& text, const gfx::FontList& font_list, float available_pixel_width, ElideBehavior elide_behavior); -// Obsolete version. Use the above version which takes gfx::FontList. -GFX_EXPORT base::string16 ElideText(const base::string16& text, - const gfx::Font& font, - float available_pixel_width, - ElideBehavior elide_behavior); // Elide a filename to fit a given pixel width, with an emphasis on not hiding // the extension unless we have to. If filename contains a path, the path will @@ -93,45 +79,9 @@ GFX_EXPORT base::string16 ElideFilename(const base::FilePath& filename, const gfx::FontList& font_list, float available_pixel_width); -// SortedDisplayURL maintains a string from a URL suitable for display to the -// use. SortedDisplayURL also provides a function used for comparing two -// SortedDisplayURLs for use in visually ordering the SortedDisplayURLs. -// -// SortedDisplayURL is relatively cheap and supports value semantics. -class GFX_EXPORT SortedDisplayURL { - public: - SortedDisplayURL(const GURL& url, const std::string& languages); - SortedDisplayURL(); - ~SortedDisplayURL(); - - // Compares this SortedDisplayURL to |url| using |collator|. Returns a value - // < 0, = 1 or > 0 as to whether this url is less then, equal to or greater - // than the supplied url. - int Compare(const SortedDisplayURL& other, icu::Collator* collator) const; - - // Returns the display string for the URL. - const base::string16& display_url() const { return display_url_; } - - private: - // Returns everything after the host. This is used by Compare if the hosts - // match. - base::string16 AfterHost() const; - - // Host name minus 'www.'. Used by Compare. - base::string16 sort_host_; - - // End of the prefix (spec and separator) in display_url_. - size_t prefix_end_; - - base::string16 display_url_; - - DISALLOW_COPY_AND_ASSIGN(SortedDisplayURL); -}; - -// Functions to elide strings when the font information is unknown. As -// opposed to the above functions, the ElideString() and -// ElideRectangleString() functions operate in terms of character units, -// not pixels. +// Functions to elide strings when the font information is unknown. As opposed +// to the above functions, ElideString() and ElideRectangleString() operate in +// terms of character units, not pixels. // If the size of |input| is more than |max_len|, this function returns // true and |input| is shortened into |output| by removing chars in the diff --git a/chromium/ui/gfx/text_elider_unittest.cc b/chromium/ui/gfx/text_elider_unittest.cc index 5fa74cbd101..0b6bcad7f5a 100644 --- a/chromium/ui/gfx/text_elider_unittest.cc +++ b/chromium/ui/gfx/text_elider_unittest.cc @@ -15,7 +15,12 @@ #include "ui/gfx/font.h" #include "ui/gfx/font_list.h" #include "ui/gfx/text_utils.h" -#include "url/gurl.h" + +using base::ASCIIToUTF16; +using base::UTF16ToUTF8; +using base::UTF16ToWide; +using base::UTF8ToUTF16; +using base::WideToUTF16; namespace gfx { @@ -32,8 +37,8 @@ struct FileTestcase { }; struct UTF16Testcase { - const string16 input; - const string16 output; + const base::string16 input; + const base::string16 output; }; struct TestData { @@ -42,24 +47,12 @@ struct TestData { const int compare_result; }; -void RunUrlTest(Testcase* testcases, size_t num_testcases) { - static const FontList font_list; - for (size_t i = 0; i < num_testcases; ++i) { - const GURL url(testcases[i].input); - // Should we test with non-empty language list? - // That's kinda redundant with net_util_unittests. - const float available_width = - GetStringWidthF(UTF8ToUTF16(testcases[i].output), font_list); - EXPECT_EQ(UTF8ToUTF16(testcases[i].output), - ElideUrl(url, font_list, available_width, std::string())); - } -} - } // namespace // TODO(ios): This test fails on iOS because iOS version of GetStringWidthF // that calls [NSString sizeWithFont] returns the rounded string width. -#if defined(OS_IOS) +// TODO(338784): Enable this on android. +#if defined(OS_IOS) || defined(OS_ANDROID) #define MAYBE_ElideEmail DISABLED_ElideEmail #else #define MAYBE_ElideEmail ElideEmail @@ -111,16 +104,21 @@ TEST(TextEliderTest, MAYBE_ElideEmail) { const FontList font_list; for (size_t i = 0; i < arraysize(testcases); ++i) { - const string16 expected_output = UTF8ToUTF16(testcases[i].output); + const base::string16 expected_output = UTF8ToUTF16(testcases[i].output); EXPECT_EQ(expected_output, - ElideEmail( - UTF8ToUTF16(testcases[i].input), - font_list, - GetStringWidthF(expected_output, font_list))); + ElideText(UTF8ToUTF16(testcases[i].input), font_list, + GetStringWidthF(expected_output, font_list), + ELIDE_EMAIL)); } } -TEST(TextEliderTest, ElideEmailMoreSpace) { +// TODO(338784): Enable this on android. +#if defined(OS_ANDROID) +#define MAYBE_ElideEmailMoreSpace DISABLED_ElideEmailMoreSpace +#else +#define MAYBE_ElideEmailMoreSpace ElideEmailMoreSpace +#endif +TEST(TextEliderTest, MAYBE_ElideEmailMoreSpace) { const int test_width_factors[] = { 100, 10000, @@ -139,141 +137,17 @@ TEST(TextEliderTest, ElideEmailMoreSpace) { font_list.GetExpectedTextWidth(test_width_factors[i]); for (size_t j = 0; j < arraysize(test_emails); ++j) { // Extra space is available: the email should not be elided. - const string16 test_email = UTF8ToUTF16(test_emails[j]); - EXPECT_EQ(test_email, ElideEmail(test_email, font_list, test_width)); + const base::string16 test_email = UTF8ToUTF16(test_emails[j]); + EXPECT_EQ(test_email, + ElideText(test_email, font_list, test_width, ELIDE_EMAIL)); } } } -// Test eliding of commonplace URLs. -TEST(TextEliderTest, TestGeneralEliding) { - const std::string kEllipsisStr(kEllipsis); - Testcase testcases[] = { - {"http://www.google.com/intl/en/ads/", - "www.google.com/intl/en/ads/"}, - {"http://www.google.com/intl/en/ads/", "www.google.com/intl/en/ads/"}, - {"http://www.google.com/intl/en/ads/", - "google.com/intl/" + kEllipsisStr + "/ads/"}, - {"http://www.google.com/intl/en/ads/", - "google.com/" + kEllipsisStr + "/ads/"}, - {"http://www.google.com/intl/en/ads/", "google.com/" + kEllipsisStr}, - {"http://www.google.com/intl/en/ads/", "goog" + kEllipsisStr}, - {"https://subdomain.foo.com/bar/filename.html", - "subdomain.foo.com/bar/filename.html"}, - {"https://subdomain.foo.com/bar/filename.html", - "subdomain.foo.com/" + kEllipsisStr + "/filename.html"}, - {"http://subdomain.foo.com/bar/filename.html", - kEllipsisStr + "foo.com/" + kEllipsisStr + "/filename.html"}, - {"http://www.google.com/intl/en/ads/?aLongQueryWhichIsNotRequired", - "www.google.com/intl/en/ads/?aLongQ" + kEllipsisStr}, - }; - - RunUrlTest(testcases, arraysize(testcases)); -} - -// When there is very little space available, the elision code will shorten -// both path AND file name to an ellipsis - ".../...". To avoid this result, -// there is a hack in place that simply treats them as one string in this -// case. -TEST(TextEliderTest, TestTrailingEllipsisSlashEllipsisHack) { - const std::string kEllipsisStr(kEllipsis); - - // Very little space, would cause double ellipsis. - FontList font_list; - GURL url("http://battersbox.com/directory/foo/peter_paul_and_mary.html"); - float available_width = GetStringWidthF( - UTF8ToUTF16("battersbox.com/" + kEllipsisStr + "/" + kEllipsisStr), - font_list); - - // Create the expected string, after elision. Depending on font size, the - // directory might become /dir... or /di... or/d... - it never should be - // shorter than that. (If it is, the font considers d... to be longer - // than .../... - that should never happen). - ASSERT_GT(GetStringWidthF(UTF8ToUTF16(kEllipsisStr + "/" + kEllipsisStr), - font_list), - GetStringWidthF(UTF8ToUTF16("d" + kEllipsisStr), font_list)); - GURL long_url("http://battersbox.com/directorynameisreallylongtoforcetrunc"); - string16 expected = - ElideUrl(long_url, font_list, available_width, std::string()); - // Ensure that the expected result still contains part of the directory name. - ASSERT_GT(expected.length(), std::string("battersbox.com/d").length()); - EXPECT_EQ(expected, - ElideUrl(url, font_list, available_width, std::string())); - - // More space available - elide directories, partially elide filename. - Testcase testcases[] = { - {"http://battersbox.com/directory/foo/peter_paul_and_mary.html", - "battersbox.com/" + kEllipsisStr + "/peter" + kEllipsisStr}, - }; - RunUrlTest(testcases, arraysize(testcases)); -} - -// Test eliding of empty strings, URLs with ports, passwords, queries, etc. -TEST(TextEliderTest, TestMoreEliding) { - const std::string kEllipsisStr(kEllipsis); - Testcase testcases[] = { - {"http://www.google.com/foo?bar", "www.google.com/foo?bar"}, - {"http://xyz.google.com/foo?bar", "xyz.google.com/foo?" + kEllipsisStr}, - {"http://xyz.google.com/foo?bar", "xyz.google.com/foo" + kEllipsisStr}, - {"http://xyz.google.com/foo?bar", "xyz.google.com/fo" + kEllipsisStr}, - {"http://a.b.com/pathname/c?d", "a.b.com/" + kEllipsisStr + "/c?d"}, - {"", ""}, - {"http://foo.bar..example.com...hello/test/filename.html", - "foo.bar..example.com...hello/" + kEllipsisStr + "/filename.html"}, - {"http://foo.bar../", "foo.bar.."}, - {"http://xn--1lq90i.cn/foo", "\xe5\x8c\x97\xe4\xba\xac.cn/foo"}, - {"http://me:mypass@secrethost.com:99/foo?bar#baz", - "secrethost.com:99/foo?bar#baz"}, - {"http://me:mypass@ss%xxfdsf.com/foo", "ss%25xxfdsf.com/foo"}, - {"mailto:elgoato@elgoato.com", "mailto:elgoato@elgoato.com"}, - {"javascript:click(0)", "javascript:click(0)"}, - {"https://chess.eecs.berkeley.edu:4430/login/arbitfilename", - "chess.eecs.berkeley.edu:4430/login/arbitfilename"}, - {"https://chess.eecs.berkeley.edu:4430/login/arbitfilename", - kEllipsisStr + "berkeley.edu:4430/" + kEllipsisStr + "/arbitfilename"}, - - // Unescaping. - {"http://www/%E4%BD%A0%E5%A5%BD?q=%E4%BD%A0%E5%A5%BD#\xe4\xbd\xa0", - "www/\xe4\xbd\xa0\xe5\xa5\xbd?q=\xe4\xbd\xa0\xe5\xa5\xbd#\xe4\xbd\xa0"}, - - // Invalid unescaping for path. The ref will always be valid UTF-8. We don't - // bother to do too many edge cases, since these are handled by the escaper - // unittest. - {"http://www/%E4%A0%E5%A5%BD?q=%E4%BD%A0%E5%A5%BD#\xe4\xbd\xa0", - "www/%E4%A0%E5%A5%BD?q=\xe4\xbd\xa0\xe5\xa5\xbd#\xe4\xbd\xa0"}, - }; - - RunUrlTest(testcases, arraysize(testcases)); -} - -// Test eliding of file: URLs. -TEST(TextEliderTest, TestFileURLEliding) { - const std::string kEllipsisStr(kEllipsis); - Testcase testcases[] = { - {"file:///C:/path1/path2/path3/filename", - "file:///C:/path1/path2/path3/filename"}, - {"file:///C:/path1/path2/path3/filename", - "C:/path1/path2/path3/filename"}, -// GURL parses "file:///C:path" differently on windows than it does on posix. -#if defined(OS_WIN) - {"file:///C:path1/path2/path3/filename", - "C:/path1/path2/" + kEllipsisStr + "/filename"}, - {"file:///C:path1/path2/path3/filename", - "C:/path1/" + kEllipsisStr + "/filename"}, - {"file:///C:path1/path2/path3/filename", - "C:/" + kEllipsisStr + "/filename"}, -#endif - {"file://filer/foo/bar/file", "filer/foo/bar/file"}, - {"file://filer/foo/bar/file", "filer/foo/" + kEllipsisStr + "/file"}, - {"file://filer/foo/bar/file", "filer/" + kEllipsisStr + "/file"}, - }; - - RunUrlTest(testcases, arraysize(testcases)); -} - // TODO(ios): This test fails on iOS because iOS version of GetStringWidthF // that calls [NSString sizeWithFont] returns the rounded string width. -#if defined(OS_IOS) +// TODO(338784): Enable this on android. +#if defined(OS_IOS) || defined(OS_ANDROID) #define MAYBE_TestFilenameEliding DISABLED_TestFilenameEliding #else #define MAYBE_TestFilenameEliding TestFilenameEliding @@ -320,14 +194,20 @@ TEST(TextEliderTest, MAYBE_TestFilenameEliding) { static const FontList font_list; for (size_t i = 0; i < arraysize(testcases); ++i) { base::FilePath filepath(testcases[i].input); - string16 expected = UTF8ToUTF16(testcases[i].output); + base::string16 expected = UTF8ToUTF16(testcases[i].output); expected = base::i18n::GetDisplayStringInLTRDirectionality(expected); EXPECT_EQ(expected, ElideFilename(filepath, font_list, GetStringWidthF(UTF8ToUTF16(testcases[i].output), font_list))); } } -TEST(TextEliderTest, ElideTextTruncate) { +// TODO(338784): Enable this on android. +#if defined(OS_ANDROID) +#define MAYBE_ElideTextTruncate DISABLED_ElideTextTruncate +#else +#define MAYBE_ElideTextTruncate ElideTextTruncate +#endif +TEST(TextEliderTest, MAYBE_ElideTextTruncate) { const FontList font_list; const float kTestWidth = GetStringWidthF(ASCIIToUTF16("Test"), font_list); struct TestData { @@ -344,13 +224,19 @@ TEST(TextEliderTest, ElideTextTruncate) { }; for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { - string16 result = ElideText(UTF8ToUTF16(cases[i].input), font_list, - cases[i].width, TRUNCATE_AT_END); + base::string16 result = ElideText(UTF8ToUTF16(cases[i].input), font_list, + cases[i].width, TRUNCATE); EXPECT_EQ(cases[i].output, UTF16ToUTF8(result)); } } -TEST(TextEliderTest, ElideTextEllipsis) { +// TODO(338784): Enable this on android. +#if defined(OS_ANDROID) +#define MAYBE_ElideTextEllipsis DISABLED_ElideTextEllipsis +#else +#define MAYBE_ElideTextEllipsis ElideTextEllipsis +#endif +TEST(TextEliderTest, MAYBE_ElideTextEllipsis) { const FontList font_list; const float kTestWidth = GetStringWidthF(ASCIIToUTF16("Test"), font_list); const char* kEllipsis = "\xE2\x80\xA6"; @@ -370,79 +256,132 @@ TEST(TextEliderTest, ElideTextEllipsis) { }; for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { - string16 result = ElideText(UTF8ToUTF16(cases[i].input), font_list, - cases[i].width, ELIDE_AT_END); + base::string16 result = ElideText(UTF8ToUTF16(cases[i].input), font_list, + cases[i].width, ELIDE_TAIL); EXPECT_EQ(cases[i].output, UTF16ToUTF8(result)); } } +// TODO(338784): Enable this on android. +#if defined(OS_ANDROID) +#define MAYBE_ElideTextEllipsisFront DISABLED_ElideTextEllipsisFront +#else +#define MAYBE_ElideTextEllipsisFront ElideTextEllipsisFront +#endif +TEST(TextEliderTest, MAYBE_ElideTextEllipsisFront) { + const FontList font_list; + const float kTestWidth = GetStringWidthF(ASCIIToUTF16("Test"), font_list); + const std::string kEllipsisStr(kEllipsis); + const float kEllipsisWidth = + GetStringWidthF(UTF8ToUTF16(kEllipsis), font_list); + const float kEllipsis23Width = + GetStringWidthF(UTF8ToUTF16(kEllipsisStr + "23"), font_list); + struct TestData { + const char* input; + float width; + const base::string16 output; + } cases[] = { + { "", 0, base::string16() }, + { "Test", 0, base::string16() }, + { "Test", kEllipsisWidth, UTF8ToUTF16(kEllipsisStr) }, + { "", kTestWidth, base::string16() }, + { "Tes", kTestWidth, ASCIIToUTF16("Tes") }, + { "Test", kTestWidth, ASCIIToUTF16("Test") }, + { "Test123", kEllipsis23Width, UTF8ToUTF16(kEllipsisStr + "23") }, + }; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { + base::string16 result = ElideText(UTF8ToUTF16(cases[i].input), font_list, + cases[i].width, ELIDE_HEAD); + EXPECT_EQ(cases[i].output, result); + } +} + // Checks that all occurrences of |first_char| are followed by |second_char| and // all occurrences of |second_char| are preceded by |first_char| in |text|. -static void CheckSurrogatePairs(const string16& text, - char16 first_char, - char16 second_char) { +static void CheckSurrogatePairs(const base::string16& text, + base::char16 first_char, + base::char16 second_char) { size_t index = text.find_first_of(first_char); - while (index != string16::npos) { + while (index != base::string16::npos) { EXPECT_LT(index, text.length() - 1); EXPECT_EQ(second_char, text[index + 1]); index = text.find_first_of(first_char, index + 1); } index = text.find_first_of(second_char); - while (index != string16::npos) { + while (index != base::string16::npos) { EXPECT_GT(index, 0U); EXPECT_EQ(first_char, text[index - 1]); index = text.find_first_of(second_char, index + 1); } } -TEST(TextEliderTest, ElideTextSurrogatePairs) { +// TODO(338784): Enable this on android. +#if defined(OS_ANDROID) +#define MAYBE_ElideTextSurrogatePairs DISABLED_ElideTextSurrogatePairs +#else +#define MAYBE_ElideTextSurrogatePairs ElideTextSurrogatePairs +#endif +TEST(TextEliderTest, MAYBE_ElideTextSurrogatePairs) { const FontList font_list; // The below is 'MUSICAL SYMBOL G CLEF', which is represented in UTF-16 as // two characters forming a surrogate pair 0x0001D11E. const std::string kSurrogate = "\xF0\x9D\x84\x9E"; - const string16 kTestString = + const base::string16 kTestString = UTF8ToUTF16(kSurrogate + "ab" + kSurrogate + kSurrogate + "cd"); const float kTestStringWidth = GetStringWidthF(kTestString, font_list); - const char16 kSurrogateFirstChar = kTestString[0]; - const char16 kSurrogateSecondChar = kTestString[1]; - string16 result; + const base::char16 kSurrogateFirstChar = kTestString[0]; + const base::char16 kSurrogateSecondChar = kTestString[1]; + base::string16 result; // Elide |kTextString| to all possible widths and check that no instance of // |kSurrogate| was split in two. for (float width = 0; width <= kTestStringWidth; width++) { - result = ElideText(kTestString, font_list, width, TRUNCATE_AT_END); + result = ElideText(kTestString, font_list, width, TRUNCATE); + CheckSurrogatePairs(result, kSurrogateFirstChar, kSurrogateSecondChar); + + result = ElideText(kTestString, font_list, width, ELIDE_TAIL); CheckSurrogatePairs(result, kSurrogateFirstChar, kSurrogateSecondChar); - result = ElideText(kTestString, font_list, width, ELIDE_AT_END); + result = ElideText(kTestString, font_list, width, ELIDE_MIDDLE); CheckSurrogatePairs(result, kSurrogateFirstChar, kSurrogateSecondChar); - result = ElideText(kTestString, font_list, width, ELIDE_IN_MIDDLE); + result = ElideText(kTestString, font_list, width, ELIDE_HEAD); CheckSurrogatePairs(result, kSurrogateFirstChar, kSurrogateSecondChar); } } -TEST(TextEliderTest, ElideTextLongStrings) { - const string16 kEllipsisStr = UTF8ToUTF16(kEllipsis); - string16 data_scheme(UTF8ToUTF16("data:text/plain,")); +// TODO(338784): Enable this on android. +#if defined(OS_ANDROID) +#define MAYBE_ElideTextLongStrings DISABLED_ElideTextLongStrings +#else +#define MAYBE_ElideTextLongStrings ElideTextLongStrings +#endif +TEST(TextEliderTest, MAYBE_ElideTextLongStrings) { + const base::string16 kEllipsisStr = UTF8ToUTF16(kEllipsis); + base::string16 data_scheme(UTF8ToUTF16("data:text/plain,")); size_t data_scheme_length = data_scheme.length(); - string16 ten_a(10, 'a'); - string16 hundred_a(100, 'a'); - string16 thousand_a(1000, 'a'); - string16 ten_thousand_a(10000, 'a'); - string16 hundred_thousand_a(100000, 'a'); - string16 million_a(1000000, 'a'); + base::string16 ten_a(10, 'a'); + base::string16 hundred_a(100, 'a'); + base::string16 thousand_a(1000, 'a'); + base::string16 ten_thousand_a(10000, 'a'); + base::string16 hundred_thousand_a(100000, 'a'); + base::string16 million_a(1000000, 'a'); + + // TODO(gbillock): Improve these tests by adding more string diversity and + // doing string compares instead of length compares. See bug 338836. size_t number_of_as = 156; - string16 long_string_end( - data_scheme + string16(number_of_as, 'a') + kEllipsisStr); + base::string16 long_string_end( + data_scheme + base::string16(number_of_as, 'a') + kEllipsisStr); UTF16Testcase testcases_end[] = { - {data_scheme + ten_a, data_scheme + ten_a}, - {data_scheme + hundred_a, data_scheme + hundred_a}, - {data_scheme + thousand_a, long_string_end}, - {data_scheme + ten_thousand_a, long_string_end}, - {data_scheme + hundred_thousand_a, long_string_end}, - {data_scheme + million_a, long_string_end}, + { data_scheme + ten_a, data_scheme + ten_a }, + { data_scheme + hundred_a, data_scheme + hundred_a }, + { data_scheme + thousand_a, long_string_end }, + { data_scheme + ten_thousand_a, long_string_end }, + { data_scheme + hundred_thousand_a, long_string_end }, + { data_scheme + million_a, long_string_end }, }; const FontList font_list; @@ -451,85 +390,58 @@ TEST(TextEliderTest, ElideTextLongStrings) { // Compare sizes rather than actual contents because if the test fails, // output is rather long. EXPECT_EQ(testcases_end[i].output.size(), - ElideText( - testcases_end[i].input, - font_list, - GetStringWidthF(testcases_end[i].output, font_list), - ELIDE_AT_END).size()); + ElideText(testcases_end[i].input, font_list, + GetStringWidthF(testcases_end[i].output, font_list), + ELIDE_TAIL).size()); EXPECT_EQ(kEllipsisStr, ElideText(testcases_end[i].input, font_list, ellipsis_width, - ELIDE_AT_END)); + ELIDE_TAIL)); } size_t number_of_trailing_as = (data_scheme_length + number_of_as) / 2; - string16 long_string_middle(data_scheme + - string16(number_of_as - number_of_trailing_as, 'a') + kEllipsisStr + - string16(number_of_trailing_as, 'a')); + base::string16 long_string_middle(data_scheme + + base::string16(number_of_as - number_of_trailing_as, 'a') + kEllipsisStr + + base::string16(number_of_trailing_as, 'a')); UTF16Testcase testcases_middle[] = { - {data_scheme + ten_a, data_scheme + ten_a}, - {data_scheme + hundred_a, data_scheme + hundred_a}, - {data_scheme + thousand_a, long_string_middle}, - {data_scheme + ten_thousand_a, long_string_middle}, - {data_scheme + hundred_thousand_a, long_string_middle}, - {data_scheme + million_a, long_string_middle}, + { data_scheme + ten_a, data_scheme + ten_a }, + { data_scheme + hundred_a, data_scheme + hundred_a }, + { data_scheme + thousand_a, long_string_middle }, + { data_scheme + ten_thousand_a, long_string_middle }, + { data_scheme + hundred_thousand_a, long_string_middle }, + { data_scheme + million_a, long_string_middle }, }; for (size_t i = 0; i < arraysize(testcases_middle); ++i) { // Compare sizes rather than actual contents because if the test fails, // output is rather long. EXPECT_EQ(testcases_middle[i].output.size(), - ElideText( - testcases_middle[i].input, - font_list, - GetStringWidthF(testcases_middle[i].output, font_list), - ELIDE_AT_END).size()); + ElideText(testcases_middle[i].input, font_list, + GetStringWidthF(testcases_middle[i].output, font_list), + ELIDE_MIDDLE).size()); EXPECT_EQ(kEllipsisStr, ElideText(testcases_middle[i].input, font_list, ellipsis_width, - ELIDE_AT_END)); + ELIDE_MIDDLE)); } -} - -// Verifies display_url is set correctly. -TEST(TextEliderTest, SortedDisplayURL) { - SortedDisplayURL d_url(GURL("http://www.google.com"), std::string()); - EXPECT_EQ("www.google.com", UTF16ToASCII(d_url.display_url())); -} - -// Verifies DisplayURL::Compare works correctly. -TEST(TextEliderTest, SortedDisplayURLCompare) { - UErrorCode create_status = U_ZERO_ERROR; - scoped_ptr<icu::Collator> collator( - icu::Collator::createInstance(create_status)); - if (!U_SUCCESS(create_status)) - return; - - TestData tests[] = { - // IDN comparison. Hosts equal, so compares on path. - { "http://xn--1lq90i.cn/a", "http://xn--1lq90i.cn/b", -1}, - // Because the host and after host match, this compares the full url. - { "http://www.x/b", "http://x/b", -1 }, - - // Because the host and after host match, this compares the full url. - { "http://www.a:1/b", "http://a:1/b", 1 }, - - // The hosts match, so these end up comparing on the after host portion. - { "http://www.x:0/b", "http://x:1/b", -1 }, - { "http://www.x/a", "http://x/b", -1 }, - { "http://x/b", "http://www.x/a", 1 }, - - // Trivial Equality. - { "http://a/", "http://a/", 0 }, - - // Compares just hosts. - { "http://www.a/", "http://b/", -1 }, + base::string16 long_string_beginning( + kEllipsisStr + base::string16(number_of_as, 'a')); + UTF16Testcase testcases_beginning[] = { + { data_scheme + ten_a, data_scheme + ten_a }, + { data_scheme + hundred_a, data_scheme + hundred_a }, + { data_scheme + thousand_a, long_string_beginning }, + { data_scheme + ten_thousand_a, long_string_beginning }, + { data_scheme + hundred_thousand_a, long_string_beginning }, + { data_scheme + million_a, long_string_beginning }, }; - - for (size_t i = 0; i < arraysize(tests); ++i) { - SortedDisplayURL url1(GURL(tests[i].a), std::string()); - SortedDisplayURL url2(GURL(tests[i].b), std::string()); - EXPECT_EQ(tests[i].compare_result, url1.Compare(url2, collator.get())); - EXPECT_EQ(-tests[i].compare_result, url2.Compare(url1, collator.get())); + for (size_t i = 0; i < arraysize(testcases_beginning); ++i) { + EXPECT_EQ(testcases_beginning[i].output.size(), + ElideText( + testcases_beginning[i].input, font_list, + GetStringWidthF(testcases_beginning[i].output, font_list), + ELIDE_HEAD).size()); + EXPECT_EQ(kEllipsisStr, + ElideText(testcases_beginning[i].input, font_list, ellipsis_width, + ELIDE_HEAD)); } } @@ -553,7 +465,7 @@ TEST(TextEliderTest, ElideString) { { "Hello, my name is Tom", 100, false, "Hello, my name is Tom" } }; for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { - string16 output; + base::string16 output; EXPECT_EQ(cases[i].result, ElideString(UTF8ToUTF16(cases[i].input), cases[i].max_len, &output)); @@ -561,7 +473,13 @@ TEST(TextEliderTest, ElideString) { } } -TEST(TextEliderTest, ElideRectangleText) { +// TODO(338784): Enable this on android. +#if defined(OS_ANDROID) +#define MAYBE_ElideRectangleText DISABLED_ElideRectangleText +#else +#define MAYBE_ElideRectangleText ElideRectangleText +#endif +TEST(TextEliderTest, MAYBE_ElideRectangleText) { const FontList font_list; const int line_height = font_list.GetHeight(); const float test_width = GetStringWidthF(ASCIIToUTF16("Test"), font_list); @@ -601,7 +519,7 @@ TEST(TextEliderTest, ElideRectangleText) { }; for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { - std::vector<string16> lines; + std::vector<base::string16> lines; EXPECT_EQ(cases[i].truncated_y ? INSUFFICIENT_SPACE_VERTICAL : 0, ElideRectangleText(UTF8ToUTF16(cases[i].input), font_list, @@ -618,7 +536,14 @@ TEST(TextEliderTest, ElideRectangleText) { } } -TEST(TextEliderTest, ElideRectangleTextPunctuation) { +// TODO(338784): Enable this on android. +#if defined(OS_ANDROID) +#define MAYBE_ElideRectangleTextPunctuation \ + DISABLED_ElideRectangleTextPunctuation +#else +#define MAYBE_ElideRectangleTextPunctuation ElideRectangleTextPunctuation +#endif +TEST(TextEliderTest, MAYBE_ElideRectangleTextPunctuation) { const FontList font_list; const int line_height = font_list.GetHeight(); const float test_width = GetStringWidthF(ASCIIToUTF16("Test"), font_list); @@ -639,7 +564,7 @@ TEST(TextEliderTest, ElideRectangleTextPunctuation) { }; for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { - std::vector<string16> lines; + std::vector<base::string16> lines; const WordWrapBehavior wrap_behavior = (cases[i].wrap_words ? WRAP_LONG_WORDS : TRUNCATE_LONG_WORDS); EXPECT_EQ(cases[i].truncated_x ? INSUFFICIENT_SPACE_HORIZONTAL : 0, @@ -658,10 +583,17 @@ TEST(TextEliderTest, ElideRectangleTextPunctuation) { } } -TEST(TextEliderTest, ElideRectangleTextLongWords) { +// TODO(338784): Enable this on android. +#if defined(OS_ANDROID) +#define MAYBE_ElideRectangleTextLongWords DISABLED_ElideRectangleTextLongWords +#else +#define MAYBE_ElideRectangleTextLongWords ElideRectangleTextLongWords +#endif +TEST(TextEliderTest, MAYBE_ElideRectangleTextLongWords) { const FontList font_list; const int kAvailableHeight = 1000; - const string16 kElidedTesting = UTF8ToUTF16(std::string("Tes") + kEllipsis); + const base::string16 kElidedTesting = + UTF8ToUTF16(std::string("Tes") + kEllipsis); const float elided_width = GetStringWidthF(kElidedTesting, font_list); const float test_width = GetStringWidthF(ASCIIToUTF16("Test"), font_list); @@ -702,7 +634,7 @@ TEST(TextEliderTest, ElideRectangleTextLongWords) { }; for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { - std::vector<string16> lines; + std::vector<base::string16> lines; EXPECT_EQ(cases[i].truncated_x ? INSUFFICIENT_SPACE_HORIZONTAL : 0, ElideRectangleText(UTF8ToUTF16(cases[i].input), font_list, @@ -722,7 +654,14 @@ TEST(TextEliderTest, ElideRectangleTextLongWords) { // fail because the truncated integer width is returned for the string // and the accumulation of the truncated values causes the elide function // to wrap incorrectly. -TEST(TextEliderTest, ElideRectangleTextCheckLineWidth) { +// TODO(338784): Enable this on android. +#if defined(OS_ANDROID) +#define MAYBE_ElideRectangleTextCheckLineWidth \ + DISABLED_ElideRectangleTextCheckLineWidth +#else +#define MAYBE_ElideRectangleTextCheckLineWidth ElideRectangleTextCheckLineWidth +#endif +TEST(TextEliderTest, MAYBE_ElideRectangleTextCheckLineWidth) { FontList font_list; #if defined(OS_MACOSX) && !defined(OS_IOS) // Use a specific font to expose the line width exceeding problem. @@ -731,7 +670,7 @@ TEST(TextEliderTest, ElideRectangleTextCheckLineWidth) { const float kAvailableWidth = 235; const int kAvailableHeight = 1000; const char text[] = "that Russian place we used to go to after fencing"; - std::vector<string16> lines; + std::vector<base::string16> lines; EXPECT_EQ(0, ElideRectangleText(UTF8ToUTF16(text), font_list, kAvailableWidth, @@ -816,7 +755,7 @@ TEST(TextEliderTest, ElideRectangleString) { { "Hi, my name is\nTom", 2, 20, false, "Hi, my name is\nTom" }, { "Hi, my name is Tom", 1, 40, false, "Hi, my name is Tom" }, }; - string16 output; + base::string16 output; for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { EXPECT_EQ(cases[i].result, ElideRectangleString(UTF8ToUTF16(cases[i].input), @@ -898,7 +837,7 @@ TEST(TextEliderTest, ElideRectangleStringNotStrict) { { "Hi, my name_is\nDick", 2, 20, false, "Hi, my name_is\nDick" }, { "Hi, my name_is Dick", 1, 40, false, "Hi, my name_is Dick" }, }; - string16 output; + base::string16 output; for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { EXPECT_EQ(cases[i].result, ElideRectangleString(UTF8ToUTF16(cases[i].input), @@ -910,17 +849,17 @@ TEST(TextEliderTest, ElideRectangleStringNotStrict) { TEST(TextEliderTest, ElideRectangleWide16) { // Two greek words separated by space. - const string16 str(WideToUTF16( + const base::string16 str(WideToUTF16( L"\x03a0\x03b1\x03b3\x03ba\x03cc\x03c3\x03bc\x03b9" L"\x03bf\x03c2\x0020\x0399\x03c3\x03c4\x03cc\x03c2")); - const string16 out1(WideToUTF16( + const base::string16 out1(WideToUTF16( L"\x03a0\x03b1\x03b3\x03ba\n" L"\x03cc\x03c3\x03bc\x03b9\n" L"...")); - const string16 out2(WideToUTF16( + const base::string16 out2(WideToUTF16( L"\x03a0\x03b1\x03b3\x03ba\x03cc\x03c3\x03bc\x03b9\x03bf\x03c2\x0020\n" L"\x0399\x03c3\x03c4\x03cc\x03c2")); - string16 output; + base::string16 output; EXPECT_TRUE(ElideRectangleString(str, 2, 4, true, &output)); EXPECT_EQ(out1, output); EXPECT_FALSE(ElideRectangleString(str, 2, 12, true, &output)); @@ -929,19 +868,19 @@ TEST(TextEliderTest, ElideRectangleWide16) { TEST(TextEliderTest, ElideRectangleWide32) { // Four U+1D49C MATHEMATICAL SCRIPT CAPITAL A followed by space "aaaaa". - const string16 str(UTF8ToUTF16( + const base::string16 str(UTF8ToUTF16( "\xF0\x9D\x92\x9C\xF0\x9D\x92\x9C\xF0\x9D\x92\x9C\xF0\x9D\x92\x9C" " aaaaa")); - const string16 out(UTF8ToUTF16( + const base::string16 out(UTF8ToUTF16( "\xF0\x9D\x92\x9C\xF0\x9D\x92\x9C\xF0\x9D\x92\x9C\n" "\xF0\x9D\x92\x9C \naaa\n...")); - string16 output; + base::string16 output; EXPECT_TRUE(ElideRectangleString(str, 3, 3, true, &output)); EXPECT_EQ(out, output); } TEST(TextEliderTest, TruncateString) { - string16 string = ASCIIToUTF16("foooooey bxxxar baz"); + base::string16 string = ASCIIToUTF16("foooooey bxxxar baz"); // Make sure it doesn't modify the string if length > string length. EXPECT_EQ(string, TruncateString(string, 100)); diff --git a/chromium/ui/gfx/text_utils_unittest.cc b/chromium/ui/gfx/text_utils_unittest.cc index 1090b388b04..0c0127fc123 100644 --- a/chromium/ui/gfx/text_utils_unittest.cc +++ b/chromium/ui/gfx/text_utils_unittest.cc @@ -6,11 +6,12 @@ #include "base/strings/utf_string_conversions.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/gfx/font_list.h" namespace gfx { namespace { -const char16 kAcceleratorChar = '&'; +const base::char16 kAcceleratorChar = '&'; TEST(TextUtilsTest, RemoveAcceleratorChar) { struct TestData { @@ -48,15 +49,33 @@ TEST(TextUtilsTest, RemoveAcceleratorChar) { for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { int accelerated_char_pos; int accelerated_char_span; - base::string16 result = RemoveAcceleratorChar(UTF8ToUTF16(cases[i].input), - kAcceleratorChar, - &accelerated_char_pos, - &accelerated_char_span); - EXPECT_EQ(result, UTF8ToUTF16(cases[i].output)); + base::string16 result = RemoveAcceleratorChar( + base::UTF8ToUTF16(cases[i].input), + kAcceleratorChar, + &accelerated_char_pos, + &accelerated_char_span); + EXPECT_EQ(result, base::UTF8ToUTF16(cases[i].output)); EXPECT_EQ(accelerated_char_pos, cases[i].accelerated_char_pos); EXPECT_EQ(accelerated_char_span, cases[i].accelerated_char_span); } } +// Disabled on Ozone since there are no fonts: crbug.com/320050 +#if defined(USE_OZONE) +#define MAYBE_GetStringWidth DISABLED_GetStringWidth +#else +#define MAYBE_GetStringWidth GetStringWidth +#endif +TEST(TextUtilsTest, MAYBE_GetStringWidth) { + FontList font_list; + EXPECT_EQ(GetStringWidth(base::string16(), font_list), 0); + EXPECT_GT(GetStringWidth(base::ASCIIToUTF16("a"), font_list), + GetStringWidth(base::string16(), font_list)); + EXPECT_GT(GetStringWidth(base::ASCIIToUTF16("ab"), font_list), + GetStringWidth(base::ASCIIToUTF16("a"), font_list)); + EXPECT_GT(GetStringWidth(base::ASCIIToUTF16("abc"), font_list), + GetStringWidth(base::ASCIIToUTF16("ab"), font_list)); +} + } // namespace } // namespace gfx diff --git a/chromium/ui/gfx/transform.h b/chromium/ui/gfx/transform.h index 5e3b8303340..5440aac71f5 100644 --- a/chromium/ui/gfx/transform.h +++ b/chromium/ui/gfx/transform.h @@ -89,6 +89,9 @@ class GFX_EXPORT Transform { // to |this|. void Scale(SkMScalar x, SkMScalar y); void Scale3d(SkMScalar x, SkMScalar y, SkMScalar z); + gfx::Vector2dF Scale2d() const { + return gfx::Vector2dF(matrix_.get(0, 0), matrix_.get(1, 1)); + } // Applies the current transformation on a translation and assigns the result // to |this|. @@ -136,6 +139,11 @@ class GFX_EXPORT Transform { // translation. bool IsIdentityOrIntegerTranslation() const; + // Returns true if the matrix had only scaling components. + bool IsScale2d() const { + return !(matrix_.getType() & ~SkMatrix44::kScale_Mask); + } + // Returns true if the matrix is has only scaling and translation components. bool IsScaleOrTranslation() const { int mask = SkMatrix44::kScale_Mask | SkMatrix44::kTranslate_Mask; diff --git a/chromium/ui/gfx/utf16_indexing_unittest.cc b/chromium/ui/gfx/utf16_indexing_unittest.cc index da2f8f5b56c..f93d6693660 100644 --- a/chromium/ui/gfx/utf16_indexing_unittest.cc +++ b/chromium/ui/gfx/utf16_indexing_unittest.cc @@ -9,8 +9,9 @@ namespace gfx { TEST(UTF16IndexingTest, IndexOffsetConversions) { // Valid surrogate pair surrounded by unpaired surrogates - const char16 foo[] = {0xDC00, 0xD800, 0xD800, 0xDFFF, 0xDFFF, 0xDBFF, 0}; - const string16 s(foo); + const base::char16 foo[] = + {0xDC00, 0xD800, 0xD800, 0xDFFF, 0xDFFF, 0xDBFF, 0}; + const base::string16 s(foo); const size_t the_invalid_index = 3; for (size_t i = 0; i <= s.length(); ++i) EXPECT_EQ(i != the_invalid_index, IsValidCodePointIndex(s, i)); diff --git a/chromium/ui/gfx/vector2d.h b/chromium/ui/gfx/vector2d.h index 8dd76324d1a..c0eefd23943 100644 --- a/chromium/ui/gfx/vector2d.h +++ b/chromium/ui/gfx/vector2d.h @@ -1,91 +1,7 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Defines a simple integer vector class. This class is used to indicate a -// distance in two dimensions between two points. Subtracting two points should -// produce a vector, and adding a vector to a point produces the point at the -// vector's distance from the original point. +// TODO(beng): remove once callsites are patched. +#include "ui/gfx/geometry/vector2d.h" -#ifndef UI_GFX_VECTOR2D_H_ -#define UI_GFX_VECTOR2D_H_ - -#include <string> - -#include "base/basictypes.h" -#include "ui/gfx/gfx_export.h" -#include "ui/gfx/vector2d_f.h" - -namespace gfx { - -class GFX_EXPORT Vector2d { - public: - Vector2d() : x_(0), y_(0) {} - Vector2d(int x, int y) : x_(x), y_(y) {} - - int x() const { return x_; } - void set_x(int x) { x_ = x; } - - int y() const { return y_; } - void set_y(int y) { y_ = y; } - - // True if both components of the vector are 0. - bool IsZero() const; - - // Add the components of the |other| vector to the current vector. - void Add(const Vector2d& other); - // Subtract the components of the |other| vector from the current vector. - void Subtract(const Vector2d& other); - - void operator+=(const Vector2d& other) { Add(other); } - void operator-=(const Vector2d& other) { Subtract(other); } - - void SetToMin(const Vector2d& other) { - x_ = x_ <= other.x_ ? x_ : other.x_; - y_ = y_ <= other.y_ ? y_ : other.y_; - } - - void SetToMax(const Vector2d& other) { - x_ = x_ >= other.x_ ? x_ : other.x_; - y_ = y_ >= other.y_ ? y_ : other.y_; - } - - // Gives the square of the diagonal length of the vector. Since this is - // cheaper to compute than Length(), it is useful when you want to compare - // relative lengths of different vectors without needing the actual lengths. - int64 LengthSquared() const; - // Gives the diagonal length of the vector. - float Length() const; - - std::string ToString() const; - - operator Vector2dF() const { return Vector2dF(x_, y_); } - - private: - int x_; - int y_; -}; - -inline bool operator==(const Vector2d& lhs, const Vector2d& rhs) { - return lhs.x() == rhs.x() && lhs.y() == rhs.y(); -} - -inline Vector2d operator-(const Vector2d& v) { - return Vector2d(-v.x(), -v.y()); -} - -inline Vector2d operator+(const Vector2d& lhs, const Vector2d& rhs) { - Vector2d result = lhs; - result.Add(rhs); - return result; -} - -inline Vector2d operator-(const Vector2d& lhs, const Vector2d& rhs) { - Vector2d result = lhs; - result.Add(-rhs); - return result; -} - -} // namespace gfx - -#endif // UI_GFX_VECTOR2D_H_ diff --git a/chromium/ui/gfx/vector2d_conversions.h b/chromium/ui/gfx/vector2d_conversions.h index 509a4567d10..5417e1c3f21 100644 --- a/chromium/ui/gfx/vector2d_conversions.h +++ b/chromium/ui/gfx/vector2d_conversions.h @@ -1,24 +1,7 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_GFX_VECTOR2D_CONVERSIONS_H_ -#define UI_GFX_VECTOR2D_CONVERSIONS_H_ +// TODO(beng): remove once callsites are patched. +#include "ui/gfx/geometry/vector2d_conversions.h" -#include "ui/gfx/vector2d.h" -#include "ui/gfx/vector2d_f.h" - -namespace gfx { - -// Returns a Vector2d with each component from the input Vector2dF floored. -GFX_EXPORT Vector2d ToFlooredVector2d(const Vector2dF& vector2d); - -// Returns a Vector2d with each component from the input Vector2dF ceiled. -GFX_EXPORT Vector2d ToCeiledVector2d(const Vector2dF& vector2d); - -// Returns a Vector2d with each component from the input Vector2dF rounded. -GFX_EXPORT Vector2d ToRoundedVector2d(const Vector2dF& vector2d); - -} // namespace gfx - -#endif // UI_GFX_VECTOR2D_CONVERSIONS_H_ diff --git a/chromium/ui/gfx/vector2d_f.h b/chromium/ui/gfx/vector2d_f.h index 289b9b7a8d1..28a51d3da85 100644 --- a/chromium/ui/gfx/vector2d_f.h +++ b/chromium/ui/gfx/vector2d_f.h @@ -1,112 +1,7 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Defines a simple float vector class. This class is used to indicate a -// distance in two dimensions between two points. Subtracting two points should -// produce a vector, and adding a vector to a point produces the point at the -// vector's distance from the original point. +// TODO(beng): remove once callsites are patched. +#include "ui/gfx/geometry/vector2d_f.h" -#ifndef UI_GFX_VECTOR2D_F_H_ -#define UI_GFX_VECTOR2D_F_H_ - -#include <string> - -#include "ui/gfx/gfx_export.h" - -namespace gfx { - -class GFX_EXPORT Vector2dF { - public: - Vector2dF() : x_(0), y_(0) {} - Vector2dF(float x, float y) : x_(x), y_(y) {} - - float x() const { return x_; } - void set_x(float x) { x_ = x; } - - float y() const { return y_; } - void set_y(float y) { y_ = y; } - - // True if both components of the vector are 0. - bool IsZero() const; - - // Add the components of the |other| vector to the current vector. - void Add(const Vector2dF& other); - // Subtract the components of the |other| vector from the current vector. - void Subtract(const Vector2dF& other); - - void operator+=(const Vector2dF& other) { Add(other); } - void operator-=(const Vector2dF& other) { Subtract(other); } - - void SetToMin(const Vector2dF& other) { - x_ = x_ <= other.x_ ? x_ : other.x_; - y_ = y_ <= other.y_ ? y_ : other.y_; - } - - void SetToMax(const Vector2dF& other) { - x_ = x_ >= other.x_ ? x_ : other.x_; - y_ = y_ >= other.y_ ? y_ : other.y_; - } - - // Gives the square of the diagonal length of the vector. - double LengthSquared() const; - // Gives the diagonal length of the vector. - float Length() const; - - // Scale the x and y components of the vector by |scale|. - void Scale(float scale) { Scale(scale, scale); } - // Scale the x and y components of the vector by |x_scale| and |y_scale| - // respectively. - void Scale(float x_scale, float y_scale); - - std::string ToString() const; - - private: - float x_; - float y_; -}; - -inline bool operator==(const Vector2dF& lhs, const Vector2dF& rhs) { - return lhs.x() == rhs.x() && lhs.y() == rhs.y(); -} - -inline bool operator!=(const Vector2dF& lhs, const Vector2dF& rhs) { - return !(lhs == rhs); -} - -inline Vector2dF operator-(const Vector2dF& v) { - return Vector2dF(-v.x(), -v.y()); -} - -inline Vector2dF operator+(const Vector2dF& lhs, const Vector2dF& rhs) { - Vector2dF result = lhs; - result.Add(rhs); - return result; -} - -inline Vector2dF operator-(const Vector2dF& lhs, const Vector2dF& rhs) { - Vector2dF result = lhs; - result.Add(-rhs); - return result; -} - -// Return the cross product of two vectors. -GFX_EXPORT double CrossProduct(const Vector2dF& lhs, const Vector2dF& rhs); - -// Return the dot product of two vectors. -GFX_EXPORT double DotProduct(const Vector2dF& lhs, const Vector2dF& rhs); - -// Return a vector that is |v| scaled by the given scale factors along each -// axis. -GFX_EXPORT Vector2dF ScaleVector2d(const Vector2dF& v, - float x_scale, - float y_scale); - -// Return a vector that is |v| scaled by the given scale factor. -inline Vector2dF ScaleVector2d(const Vector2dF& v, float scale) { - return ScaleVector2d(v, scale, scale); -} - -} // namespace gfx - -#endif // UI_GFX_VECTOR2D_F_H_ diff --git a/chromium/ui/gfx/vector3d_f.h b/chromium/ui/gfx/vector3d_f.h index 0e91a362eca..ad0445fc258 100644 --- a/chromium/ui/gfx/vector3d_f.h +++ b/chromium/ui/gfx/vector3d_f.h @@ -1,124 +1,7 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Defines a simple float vector class. This class is used to indicate a -// distance in two dimensions between two points. Subtracting two points should -// produce a vector, and adding a vector to a point produces the point at the -// vector's distance from the original point. +// TODO(beng): remove once callsites are patched. +#include "ui/gfx/geometry/vector3d_f.h" -#ifndef UI_GFX_VECTOR3D_F_H_ -#define UI_GFX_VECTOR3D_F_H_ - -#include <string> - -#include "ui/gfx/gfx_export.h" -#include "ui/gfx/vector2d_f.h" - -namespace gfx { - -class GFX_EXPORT Vector3dF { - public: - Vector3dF(); - Vector3dF(float x, float y, float z); - - explicit Vector3dF(const Vector2dF& other); - - float x() const { return x_; } - void set_x(float x) { x_ = x; } - - float y() const { return y_; } - void set_y(float y) { y_ = y; } - - float z() const { return z_; } - void set_z(float z) { z_ = z; } - - // True if all components of the vector are 0. - bool IsZero() const; - - // Add the components of the |other| vector to the current vector. - void Add(const Vector3dF& other); - // Subtract the components of the |other| vector from the current vector. - void Subtract(const Vector3dF& other); - - void operator+=(const Vector3dF& other) { Add(other); } - void operator-=(const Vector3dF& other) { Subtract(other); } - - void SetToMin(const Vector3dF& other) { - x_ = x_ <= other.x_ ? x_ : other.x_; - y_ = y_ <= other.y_ ? y_ : other.y_; - z_ = z_ <= other.z_ ? z_ : other.z_; - } - - void SetToMax(const Vector3dF& other) { - x_ = x_ >= other.x_ ? x_ : other.x_; - y_ = y_ >= other.y_ ? y_ : other.y_; - z_ = z_ >= other.z_ ? z_ : other.z_; - } - - // Gives the square of the diagonal length of the vector. - double LengthSquared() const; - // Gives the diagonal length of the vector. - float Length() const; - - // Scale all components of the vector by |scale|. - void Scale(float scale) { Scale(scale, scale, scale); } - // Scale the each component of the vector by the given scale factors. - void Scale(float x_scale, float y_scale, float z_scale); - - // Take the cross product of this vector with |other| and become the result. - void Cross(const Vector3dF& other); - - std::string ToString() const; - - private: - float x_; - float y_; - float z_; -}; - -inline bool operator==(const Vector3dF& lhs, const Vector3dF& rhs) { - return lhs.x() == rhs.x() && lhs.y() == rhs.y() && lhs.z() == rhs.z(); -} - -inline Vector3dF operator-(const Vector3dF& v) { - return Vector3dF(-v.x(), -v.y(), -v.z()); -} - -inline Vector3dF operator+(const Vector3dF& lhs, const Vector3dF& rhs) { - Vector3dF result = lhs; - result.Add(rhs); - return result; -} - -inline Vector3dF operator-(const Vector3dF& lhs, const Vector3dF& rhs) { - Vector3dF result = lhs; - result.Add(-rhs); - return result; -} - -// Return the cross product of two vectors. -inline Vector3dF CrossProduct(const Vector3dF& lhs, const Vector3dF& rhs) { - Vector3dF result = lhs; - result.Cross(rhs); - return result; -} - -// Return the dot product of two vectors. -GFX_EXPORT float DotProduct(const Vector3dF& lhs, const Vector3dF& rhs); - -// Return a vector that is |v| scaled by the given scale factors along each -// axis. -GFX_EXPORT Vector3dF ScaleVector3d(const Vector3dF& v, - float x_scale, - float y_scale, - float z_scale); - -// Return a vector that is |v| scaled by the given scale factor. -inline Vector3dF ScaleVector3d(const Vector3dF& v, float scale) { - return ScaleVector3d(v, scale, scale, scale); -} - -} // namespace gfx - -#endif // UI_GFX_VECTOR3D_F_H_ diff --git a/chromium/ui/gfx/win/dpi.cc b/chromium/ui/gfx/win/dpi.cc index 6bc25dee646..e562a086c9a 100644 --- a/chromium/ui/gfx/win/dpi.cc +++ b/chromium/ui/gfx/win/dpi.cc @@ -20,6 +20,8 @@ namespace { int kDefaultDPIX = 96; int kDefaultDPIY = 96; +bool force_highdpi_for_testing = false; + BOOL IsProcessDPIAwareWrapper() { typedef BOOL(WINAPI *IsProcessDPIAwarePtr)(VOID); IsProcessDPIAwarePtr is_process_dpi_aware_func = @@ -33,33 +35,81 @@ BOOL IsProcessDPIAwareWrapper() { float g_device_scale_factor = 0.0f; float GetUnforcedDeviceScaleFactor() { + // If the global device scale factor is initialized use it. This is to ensure + // we use the same scale factor across all callsites. We don't use the + // GetDeviceScaleFactor function here because it fires a DCHECK if the + // g_device_scale_factor global is 0. + if (g_device_scale_factor) + return g_device_scale_factor; return static_cast<float>(gfx::GetDPI().width()) / static_cast<float>(kDefaultDPIX); } -float GetModernUIScaleWrapper() { - float result = 1.0f; - typedef float(WINAPI *GetModernUIScalePtr)(VOID); - HMODULE lib = LoadLibraryA("metro_driver.dll"); - if (lib) { - GetModernUIScalePtr func = - reinterpret_cast<GetModernUIScalePtr>( - GetProcAddress(lib, "GetModernUIScale")); - if (func) - result = func(); - FreeLibrary(lib); +// Duplicated from Win8.1 SDK ShellScalingApi.h +typedef enum PROCESS_DPI_AWARENESS { + PROCESS_DPI_UNAWARE = 0, + PROCESS_SYSTEM_DPI_AWARE = 1, + PROCESS_PER_MONITOR_DPI_AWARE = 2 +} PROCESS_DPI_AWARENESS; + +typedef enum MONITOR_DPI_TYPE { + MDT_EFFECTIVE_DPI = 0, + MDT_ANGULAR_DPI = 1, + MDT_RAW_DPI = 2, + MDT_DEFAULT = MDT_EFFECTIVE_DPI +} MONITOR_DPI_TYPE; + +// Win8.1 supports monitor-specific DPI scaling. +bool SetProcessDpiAwarenessWrapper(PROCESS_DPI_AWARENESS value) { + typedef BOOL(WINAPI *SetProcessDpiAwarenessPtr)(PROCESS_DPI_AWARENESS); + SetProcessDpiAwarenessPtr set_process_dpi_awareness_func = + reinterpret_cast<SetProcessDpiAwarenessPtr>( + GetProcAddress(GetModuleHandleA("user32.dll"), + "SetProcessDpiAwarenessInternal")); + if (set_process_dpi_awareness_func) { + HRESULT hr = set_process_dpi_awareness_func(value); + if (SUCCEEDED(hr)) { + VLOG(1) << "SetProcessDpiAwareness succeeded."; + return true; + } else if (hr == E_ACCESSDENIED) { + LOG(ERROR) << "Access denied error from SetProcessDpiAwareness. " + "Function called twice, or manifest was used."; + } } - return result; + return false; +} + +// This function works for Windows Vista through Win8. Win8.1 must use +// SetProcessDpiAwareness[Wrapper] +BOOL SetProcessDPIAwareWrapper() { + typedef BOOL(WINAPI *SetProcessDPIAwarePtr)(VOID); + SetProcessDPIAwarePtr set_process_dpi_aware_func = + reinterpret_cast<SetProcessDPIAwarePtr>( + GetProcAddress(GetModuleHandleA("user32.dll"), + "SetProcessDPIAware")); + return set_process_dpi_aware_func && + set_process_dpi_aware_func(); +} + +DWORD ReadRegistryValue(HKEY root, + const wchar_t* base_key, + const wchar_t* value_name, + DWORD default_value) { + base::win::RegKey reg_key(HKEY_CURRENT_USER, + base_key, + KEY_QUERY_VALUE); + DWORD value; + if (reg_key.Valid() && + reg_key.ReadValueDW(value_name, &value) == ERROR_SUCCESS) { + return value; + } + return default_value; } } // namespace namespace gfx { -float GetModernUIScale() { - return GetModernUIScaleWrapper(); -} - void InitDeviceScaleFactor(float scale) { DCHECK_NE(0.0f, scale); g_device_scale_factor = scale; @@ -91,14 +141,18 @@ float GetDPIScale() { return 1.0; } +void ForceHighDPISupportForTesting(float scale) { + g_device_scale_factor = scale; +} + bool IsHighDPIEnabled() { + // Flag stored in HKEY_CURRENT_USER\SOFTWARE\\Google\\Chrome\\Profile, + // under the DWORD value high-dpi-support. // Default is disabled. - if (CommandLine::ForCurrentProcess()->HasSwitch( - switches::kHighDPISupport)) { - return CommandLine::ForCurrentProcess()->GetSwitchValueASCII( - switches::kHighDPISupport).compare("1") == 0; - } - return false; + static DWORD value = ReadRegistryValue( + HKEY_CURRENT_USER, gfx::win::kRegistryProfilePath, + gfx::win::kHighDPISupportW, TRUE); + return value != 0; } bool IsInHighDPIMode() { @@ -107,31 +161,25 @@ bool IsInHighDPIMode() { void EnableHighDPISupport() { if (IsHighDPIEnabled() && - (base::win::GetVersion() < base::win::VERSION_WIN8_1)) { - typedef BOOL(WINAPI *SetProcessDPIAwarePtr)(VOID); - SetProcessDPIAwarePtr set_process_dpi_aware_func = - reinterpret_cast<SetProcessDPIAwarePtr>( - GetProcAddress(GetModuleHandleA("user32.dll"), - "SetProcessDPIAware")); - if (set_process_dpi_aware_func) - set_process_dpi_aware_func(); + !SetProcessDpiAwarenessWrapper(PROCESS_SYSTEM_DPI_AWARE)) { + SetProcessDPIAwareWrapper(); } } namespace win { +GFX_EXPORT const wchar_t kRegistryProfilePath[] = + L"Software\\Google\\Chrome\\Profile"; +GFX_EXPORT const wchar_t kHighDPISupportW[] = L"high-dpi-support"; + float GetDeviceScaleFactor() { DCHECK_NE(0.0f, g_device_scale_factor); return g_device_scale_factor; } Point ScreenToDIPPoint(const Point& pixel_point) { - static float scaling_factor = - GetDeviceScaleFactor() > GetUnforcedDeviceScaleFactor() ? - 1.0f / GetDeviceScaleFactor() : - 1.0f; return ToFlooredPoint(ScalePoint(pixel_point, - scaling_factor)); + 1.0f / GetDeviceScaleFactor())); } Point DIPToScreenPoint(const Point& dip_point) { @@ -145,9 +193,17 @@ Rect ScreenToDIPRect(const Rect& pixel_bounds) { } Rect DIPToScreenRect(const Rect& dip_bounds) { - // TODO(kevers): Switch to non-deprecated method for float to int conversions. - return ToFlooredRectDeprecated( - ScaleRect(dip_bounds, GetDeviceScaleFactor())); + // We scale the origin by the scale factor and round up via ceil. This + // ensures that we get the original logical origin back when we scale down. + // We round the size down after scaling. It may be better to round this up + // on the same lines as the origin. + // TODO(ananta) + // Investigate if rounding size up on the same lines as origin is workable. + return gfx::Rect( + gfx::ToCeiledPoint(gfx::ScalePoint( + dip_bounds.origin(), GetDeviceScaleFactor())), + gfx::ToFlooredSize(gfx::ScaleSize( + dip_bounds.size(), GetDeviceScaleFactor()))); } Size ScreenToDIPSize(const Size& size_in_pixels) { @@ -164,31 +220,8 @@ int GetSystemMetricsInDIP(int metric) { GetDeviceScaleFactor() + 0.5); } -double GetUndocumentedDPIScale() { - // TODO(girard): Remove this code when chrome is DPIAware. - static double scale = -1.0; - if (scale == -1.0) { - scale = 1.0; - if (!IsProcessDPIAwareWrapper()) { - base::win::RegKey key(HKEY_CURRENT_USER, - L"Control Panel\\Desktop\\WindowMetrics", - KEY_QUERY_VALUE); - if (key.Valid()) { - DWORD value = 0; - if (key.ReadValueDW(L"AppliedDPI", &value) == ERROR_SUCCESS) { - scale = static_cast<double>(value) / kDefaultDPIX; - } - } - } - } - return scale; -} - -double GetUndocumentedDPITouchScale() { - static double scale = - (base::win::GetVersion() < base::win::VERSION_WIN8_1) ? - GetUndocumentedDPIScale() : 1.0; - return scale; +bool IsDeviceScaleFactorSet() { + return g_device_scale_factor != 0.0f; } } // namespace win diff --git a/chromium/ui/gfx/win/dpi.h b/chromium/ui/gfx/win/dpi.h index 772944f50f9..9dcf3802456 100644 --- a/chromium/ui/gfx/win/dpi.h +++ b/chromium/ui/gfx/win/dpi.h @@ -22,14 +22,9 @@ GFX_EXPORT void InitDeviceScaleFactor(float scale); GFX_EXPORT Size GetDPI(); // Gets the scale factor of the display. For example, if the display DPI is -// 96 then the scale factor is 1.0. Note that this is the "desktop" scale, which -// may be differnt than GetModernUIScale(). +// 96 then the scale factor is 1.0. GFX_EXPORT float GetDPIScale(); -// Gets the scale factor of the modern (metro) UI display. Returns 1.0 for -// unscaled or "not running on win8+" -GFX_EXPORT float GetModernUIScale(); - // Tests to see if the command line flag "--high-dpi-support" is set. GFX_EXPORT bool IsHighDPIEnabled(); @@ -37,6 +32,8 @@ GFX_EXPORT bool IsInHighDPIMode(); GFX_EXPORT void EnableHighDPISupport(); +GFX_EXPORT void ForceHighDPISupportForTesting(float scale); + // TODO(kevers|girard): Move above methods into win namespace. namespace win { @@ -59,18 +56,12 @@ GFX_EXPORT Size DIPToScreenSize(const Size& dip_size); // GetSystemMetrics for the given |metric|, then converts the result to DIP. GFX_EXPORT int GetSystemMetricsInDIP(int metric); -// Sometimes the OS secretly scales apps that are not DPIAware. This is not -// visible through standard OS calls like GetWindowPos(), or through -// GetDPIScale(). -// Returns the scale factor of the display, where 96 DPI is 1.0. -// (Avoid this function... use GetDPIScale() instead.) -// TODO(girard): Remove this once DPIAware is enabled - http://crbug.com/149881 -GFX_EXPORT double GetUndocumentedDPIScale(); - -// Win7 and Win8 send touch events scaled according to the current DPI -// scaling. Win8.1 corrects this, and sends touch events in DPI units. -// This function returns the appropriate scaling factor for touch events. -GFX_EXPORT double GetUndocumentedDPITouchScale(); +// Returns true if the global device scale factor has been explicitly set for +// the process. +GFX_EXPORT bool IsDeviceScaleFactorSet(); + +GFX_EXPORT extern const wchar_t kRegistryProfilePath[]; +GFX_EXPORT extern const wchar_t kHighDPISupportW[]; } // namespace win } // namespace gfx diff --git a/chromium/ui/gfx/win/hwnd_util.cc b/chromium/ui/gfx/win/hwnd_util.cc index fec39d4fe59..050aa9b733f 100644 --- a/chromium/ui/gfx/win/hwnd_util.cc +++ b/chromium/ui/gfx/win/hwnd_util.cc @@ -24,7 +24,7 @@ void AdjustWindowToFit(HWND hwnd, const RECT& bounds, bool fit_to_monitor) { if (hmon) { MONITORINFO mi; mi.cbSize = sizeof(mi); - base::win::GetMonitorInfoWrapper(hmon, &mi); + GetMonitorInfo(hmon, &mi); Rect window_rect(bounds); Rect monitor_rect(mi.rcWork); Rect new_window_rect = window_rect; @@ -54,23 +54,23 @@ void AdjustWindowToFit(HWND hwnd, const RECT& bounds, bool fit_to_monitor) { MSVC_DISABLE_OPTIMIZE(); void CrashOutOfMemory() { - LOG_GETLASTERROR(FATAL); + PLOG(FATAL); } void CrashAccessDenied() { - LOG_GETLASTERROR(FATAL); + PLOG(FATAL); } // Crash isn't one of the ones we commonly see. void CrashOther() { - LOG_GETLASTERROR(FATAL); + PLOG(FATAL); } MSVC_ENABLE_OPTIMIZE(); } // namespace -string16 GetClassName(HWND window) { +base::string16 GetClassName(HWND window) { // GetClassNameW will return a truncated result (properly null terminated) if // the given buffer is not large enough. So, it is not possible to determine // that we got the entire class name if the result is exactly equal to the @@ -150,7 +150,7 @@ void CenterAndSizeWindow(HWND parent, if (monitor) { MONITORINFO mi = {0}; mi.cbSize = sizeof(mi); - base::win::GetMonitorInfoWrapper(monitor, &mi); + GetMonitorInfo(monitor, &mi); center_bounds = mi.rcWork; } else { NOTREACHED() << "Unable to get default monitor"; @@ -200,16 +200,17 @@ void CheckWindowCreated(HWND hwnd) { CrashOther(); break; } - LOG_GETLASTERROR(FATAL); + PLOG(FATAL); } } void ShowSystemMenu(HWND window) { RECT rect; GetWindowRect(window, &rect); - Point point = Point(rect.left, rect.top); + Point point = Point(base::i18n::IsRTL() ? rect.right : rect.left, rect.top); static const int kSystemMenuOffset = 10; - point.Offset(kSystemMenuOffset, kSystemMenuOffset); + point.Offset(base::i18n::IsRTL() ? -kSystemMenuOffset : kSystemMenuOffset, + kSystemMenuOffset); ShowSystemMenuAtPoint(window, point); } diff --git a/chromium/ui/gfx/win/hwnd_util.h b/chromium/ui/gfx/win/hwnd_util.h index 472daccd82e..2afd660f5d8 100644 --- a/chromium/ui/gfx/win/hwnd_util.h +++ b/chromium/ui/gfx/win/hwnd_util.h @@ -15,8 +15,8 @@ class Point; class Size; // A version of the GetClassNameW API that returns the class name in an -// string16. An empty result indicates a failure to get the class name. -GFX_EXPORT string16 GetClassName(HWND hwnd); +// base::string16. An empty result indicates a failure to get the class name. +GFX_EXPORT base::string16 GetClassName(HWND hwnd); // Useful for subclassing a HWND. Returns the previous window procedure. GFX_EXPORT WNDPROC SetWindowProc(HWND hwnd, WNDPROC wndproc); diff --git a/chromium/ui/gfx/win/msg_util.h b/chromium/ui/gfx/win/msg_util.h new file mode 100644 index 00000000000..a5d241b1234 --- /dev/null +++ b/chromium/ui/gfx/win/msg_util.h @@ -0,0 +1,2283 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_GFX_WIN_MSG_UTIL_H_ +#define UI_GFX_WIN_MSG_UTIL_H_ + +#include "base/logging.h" +#include "ui/gfx/geometry/point.h" +#include "ui/gfx/geometry/size.h" + +// Based on WTL version 8.0 atlcrack.h + +// This differs from the original atlcrack.h by removing usage of CPoint, +// CSize, etc. + +/////////////////////////////////////////////////////////////////////////////// +// Message map macro for cracked handlers + +// Note about message maps with cracked handlers: +// For ATL 3.0, a message map using cracked handlers MUST use BEGIN_MSG_MAP_EX. +// For ATL 7.0 or higher, you can use BEGIN_MSG_MAP for CWindowImpl/CDialogImpl +// derived classes, +// but must use BEGIN_MSG_MAP_EX for classes that don't derive from +// CWindowImpl/CDialogImpl. + +#define CR_BEGIN_MSG_MAP_EX(theClass) \ + public: \ + BOOL m_bMsgHandled; \ + /* "handled" management for cracked handlers */ \ + BOOL IsMsgHandled() const { return m_bMsgHandled; } \ + void SetMsgHandled(BOOL bHandled) { m_bMsgHandled = bHandled; } \ + BOOL ProcessWindowMessage(HWND hWnd, \ + UINT uMsg, \ + WPARAM wParam, \ + LPARAM lParam, \ + LRESULT& lResult, \ + DWORD dwMsgMapID = 0) { \ + BOOL bOldMsgHandled = m_bMsgHandled; \ + BOOL bRet = _ProcessWindowMessage( \ + hWnd, uMsg, wParam, lParam, lResult, dwMsgMapID); \ + m_bMsgHandled = bOldMsgHandled; \ + return bRet; \ + } \ + BOOL _ProcessWindowMessage(HWND hWnd, \ + UINT uMsg, \ + WPARAM wParam, \ + LPARAM lParam, \ + LRESULT& lResult, \ + DWORD dwMsgMapID) { \ + BOOL bHandled = TRUE; \ + hWnd; \ + uMsg; \ + wParam; \ + lParam; \ + lResult; \ + bHandled; \ + switch (dwMsgMapID) { \ + case 0: + +// Replacement for atlwin.h's END_MSG_MAP for removing ATL usage. +#define CR_END_MSG_MAP() \ + break; \ + default: \ + NOTREACHED() << "Invalid message map ID: " << dwMsgMapID; \ + break; \ + } \ + return FALSE; \ + } + +#define CR_GET_X_LPARAM(lParam) ((int)(short)LOWORD(lParam)) +#define CR_GET_Y_LPARAM(lParam) ((int)(short)HIWORD(lParam)) + +/////////////////////////////////////////////////////////////////////////////// +// Standard Windows message macros + +// int OnCreate(LPCREATESTRUCT lpCreateStruct) +#define CR_MSG_WM_CREATE(func) \ + if (uMsg == WM_CREATE) { \ + SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((LPCREATESTRUCT)lParam); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// BOOL OnInitDialog(CWindow wndFocus, LPARAM lInitParam) +#define CR_MSG_WM_INITDIALOG(func) \ + if (uMsg == WM_INITDIALOG) { \ + SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((HWND)wParam, lParam); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// BOOL OnCopyData(CWindow wnd, PCOPYDATASTRUCT pCopyDataStruct) +#define CR_MSG_WM_COPYDATA(func) \ + if (uMsg == WM_COPYDATA) { \ + SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((HWND)wParam, (PCOPYDATASTRUCT)lParam); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnDestroy() +#define CR_MSG_WM_DESTROY(func) \ + if (uMsg == WM_DESTROY) { \ + SetMsgHandled(TRUE); \ + func(); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnMove(CPoint ptPos) +#define CR_MSG_WM_MOVE(func) \ + if (uMsg == WM_MOVE) { \ + SetMsgHandled(TRUE); \ + func(gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnSize(UINT nType, gfx::Size size) +#define CR_MSG_WM_SIZE(func) \ + if (uMsg == WM_SIZE) { \ + SetMsgHandled(TRUE); \ + func((UINT)wParam, \ + gfx::Size(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnActivate(UINT nState, BOOL bMinimized, CWindow wndOther) +#define CR_MSG_WM_ACTIVATE(func) \ + if (uMsg == WM_ACTIVATE) { \ + SetMsgHandled(TRUE); \ + func((UINT)LOWORD(wParam), (BOOL)HIWORD(wParam), (HWND)lParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnSetFocus(CWindow wndOld) +#define CR_MSG_WM_SETFOCUS(func) \ + if (uMsg == WM_SETFOCUS) { \ + SetMsgHandled(TRUE); \ + func((HWND)wParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnKillFocus(CWindow wndFocus) +#define CR_MSG_WM_KILLFOCUS(func) \ + if (uMsg == WM_KILLFOCUS) { \ + SetMsgHandled(TRUE); \ + func((HWND)wParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnEnable(BOOL bEnable) +#define CR_MSG_WM_ENABLE(func) \ + if (uMsg == WM_ENABLE) { \ + SetMsgHandled(TRUE); \ + func((BOOL)wParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnPaint(CDCHandle dc) +#define CR_MSG_WM_PAINT(func) \ + if (uMsg == WM_PAINT) { \ + SetMsgHandled(TRUE); \ + func((HDC)wParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnClose() +#define CR_MSG_WM_CLOSE(func) \ + if (uMsg == WM_CLOSE) { \ + SetMsgHandled(TRUE); \ + func(); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// BOOL OnQueryEndSession(UINT nSource, UINT uLogOff) +#define CR_MSG_WM_QUERYENDSESSION(func) \ + if (uMsg == WM_QUERYENDSESSION) { \ + SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((UINT)wParam, (UINT)lParam); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// BOOL OnQueryOpen() +#define CR_MSG_WM_QUERYOPEN(func) \ + if (uMsg == WM_QUERYOPEN) { \ + SetMsgHandled(TRUE); \ + lResult = (LRESULT)func(); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// BOOL OnEraseBkgnd(CDCHandle dc) +#define CR_MSG_WM_ERASEBKGND(func) \ + if (uMsg == WM_ERASEBKGND) { \ + SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((HDC)wParam); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnSysColorChange() +#define CR_MSG_WM_SYSCOLORCHANGE(func) \ + if (uMsg == WM_SYSCOLORCHANGE) { \ + SetMsgHandled(TRUE); \ + func(); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnEndSession(BOOL bEnding, UINT uLogOff) +#define CR_MSG_WM_ENDSESSION(func) \ + if (uMsg == WM_ENDSESSION) { \ + SetMsgHandled(TRUE); \ + func((BOOL)wParam, (UINT)lParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnShowWindow(BOOL bShow, UINT nStatus) +#define CR_MSG_WM_SHOWWINDOW(func) \ + if (uMsg == WM_SHOWWINDOW) { \ + SetMsgHandled(TRUE); \ + func((BOOL)wParam, (int)lParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// HBRUSH OnCtlColorEdit(CDCHandle dc, CEdit edit) +#define CR_MSG_WM_CTLCOLOREDIT(func) \ + if (uMsg == WM_CTLCOLOREDIT) { \ + SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// HBRUSH OnCtlColorListBox(CDCHandle dc, CListBox listBox) +#define CR_MSG_WM_CTLCOLORLISTBOX(func) \ + if (uMsg == WM_CTLCOLORLISTBOX) { \ + SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// HBRUSH OnCtlColorBtn(CDCHandle dc, CButton button) +#define CR_MSG_WM_CTLCOLORBTN(func) \ + if (uMsg == WM_CTLCOLORBTN) { \ + SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// HBRUSH OnCtlColorDlg(CDCHandle dc, CWindow wnd) +#define CR_MSG_WM_CTLCOLORDLG(func) \ + if (uMsg == WM_CTLCOLORDLG) { \ + SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// HBRUSH OnCtlColorScrollBar(CDCHandle dc, CScrollBar scrollBar) +#define CR_MSG_WM_CTLCOLORSCROLLBAR(func) \ + if (uMsg == WM_CTLCOLORSCROLLBAR) { \ + SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// HBRUSH OnCtlColorStatic(CDCHandle dc, CStatic wndStatic) +#define CR_MSG_WM_CTLCOLORSTATIC(func) \ + if (uMsg == WM_CTLCOLORSTATIC) { \ + SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnSettingChange(UINT uFlags, LPCTSTR lpszSection) +#define CR_MSG_WM_SETTINGCHANGE(func) \ + if (uMsg == WM_SETTINGCHANGE) { \ + SetMsgHandled(TRUE); \ + func((UINT)wParam, (LPCTSTR)lParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnDevModeChange(LPCTSTR lpDeviceName) +#define CR_MSG_WM_DEVMODECHANGE(func) \ + if (uMsg == WM_DEVMODECHANGE) { \ + SetMsgHandled(TRUE); \ + func((LPCTSTR)lParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnActivateApp(BOOL bActive, DWORD dwThreadID) +#define CR_MSG_WM_ACTIVATEAPP(func) \ + if (uMsg == WM_ACTIVATEAPP) { \ + SetMsgHandled(TRUE); \ + func((BOOL)wParam, (DWORD)lParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnFontChange() +#define CR_MSG_WM_FONTCHANGE(func) \ + if (uMsg == WM_FONTCHANGE) { \ + SetMsgHandled(TRUE); \ + func(); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnTimeChange() +#define CR_MSG_WM_TIMECHANGE(func) \ + if (uMsg == WM_TIMECHANGE) { \ + SetMsgHandled(TRUE); \ + func(); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnCancelMode() +#define CR_MSG_WM_CANCELMODE(func) \ + if (uMsg == WM_CANCELMODE) { \ + SetMsgHandled(TRUE); \ + func(); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// BOOL OnSetCursor(CWindow wnd, UINT nHitTest, UINT message) +#define CR_MSG_WM_SETCURSOR(func) \ + if (uMsg == WM_SETCURSOR) { \ + SetMsgHandled(TRUE); \ + lResult = (LRESULT)func( \ + (HWND)wParam, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam)); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// int OnMouseActivate(CWindow wndTopLevel, UINT nHitTest, UINT message) +#define CR_MSG_WM_MOUSEACTIVATE(func) \ + if (uMsg == WM_MOUSEACTIVATE) { \ + SetMsgHandled(TRUE); \ + lResult = (LRESULT)func( \ + (HWND)wParam, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam)); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnChildActivate() +#define CR_MSG_WM_CHILDACTIVATE(func) \ + if (uMsg == WM_CHILDACTIVATE) { \ + SetMsgHandled(TRUE); \ + func(); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnGetMinMaxInfo(LPMINMAXINFO lpMMI) +#define CR_MSG_WM_GETMINMAXINFO(func) \ + if (uMsg == WM_GETMINMAXINFO) { \ + SetMsgHandled(TRUE); \ + func((LPMINMAXINFO)lParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnIconEraseBkgnd(CDCHandle dc) +#define CR_MSG_WM_ICONERASEBKGND(func) \ + if (uMsg == WM_ICONERASEBKGND) { \ + SetMsgHandled(TRUE); \ + func((HDC)wParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnSpoolerStatus(UINT nStatus, UINT nJobs) +#define CR_MSG_WM_SPOOLERSTATUS(func) \ + if (uMsg == WM_SPOOLERSTATUS) { \ + SetMsgHandled(TRUE); \ + func((UINT)wParam, (UINT)LOWORD(lParam)); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct) +#define CR_MSG_WM_DRAWITEM(func) \ + if (uMsg == WM_DRAWITEM) { \ + SetMsgHandled(TRUE); \ + func((UINT)wParam, (LPDRAWITEMSTRUCT)lParam); \ + lResult = TRUE; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct) +#define CR_MSG_WM_MEASUREITEM(func) \ + if (uMsg == WM_MEASUREITEM) { \ + SetMsgHandled(TRUE); \ + func((UINT)wParam, (LPMEASUREITEMSTRUCT)lParam); \ + lResult = TRUE; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnDeleteItem(int nIDCtl, LPDELETEITEMSTRUCT lpDeleteItemStruct) +#define CR_MSG_WM_DELETEITEM(func) \ + if (uMsg == WM_DELETEITEM) { \ + SetMsgHandled(TRUE); \ + func((UINT)wParam, (LPDELETEITEMSTRUCT)lParam); \ + lResult = TRUE; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// int OnCharToItem(UINT nChar, UINT nIndex, CListBox listBox) +#define CR_MSG_WM_CHARTOITEM(func) \ + if (uMsg == WM_CHARTOITEM) { \ + SetMsgHandled(TRUE); \ + lResult = (LRESULT)func( \ + (UINT)LOWORD(wParam), (UINT)HIWORD(wParam), (HWND)lParam); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// int OnVKeyToItem(UINT nKey, UINT nIndex, CListBox listBox) +#define CR_MSG_WM_VKEYTOITEM(func) \ + if (uMsg == WM_VKEYTOITEM) { \ + SetMsgHandled(TRUE); \ + lResult = (LRESULT)func( \ + (UINT)LOWORD(wParam), (UINT)HIWORD(wParam), (HWND)lParam); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// HCURSOR OnQueryDragIcon() +#define CR_MSG_WM_QUERYDRAGICON(func) \ + if (uMsg == WM_QUERYDRAGICON) { \ + SetMsgHandled(TRUE); \ + lResult = (LRESULT)func(); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// int OnCompareItem(int nIDCtl, LPCOMPAREITEMSTRUCT lpCompareItemStruct) +#define CR_MSG_WM_COMPAREITEM(func) \ + if (uMsg == WM_COMPAREITEM) { \ + SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((UINT)wParam, (LPCOMPAREITEMSTRUCT)lParam); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnCompacting(UINT nCpuTime) +#define CR_MSG_WM_COMPACTING(func) \ + if (uMsg == WM_COMPACTING) { \ + SetMsgHandled(TRUE); \ + func((UINT)wParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// BOOL OnNcCreate(LPCREATESTRUCT lpCreateStruct) +#define CR_MSG_WM_NCCREATE(func) \ + if (uMsg == WM_NCCREATE) { \ + SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((LPCREATESTRUCT)lParam); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnNcDestroy() +#define CR_MSG_WM_NCDESTROY(func) \ + if (uMsg == WM_NCDESTROY) { \ + SetMsgHandled(TRUE); \ + func(); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// LRESULT OnNcCalcSize(BOOL bCalcValidRects, LPARAM lParam) +#define CR_MSG_WM_NCCALCSIZE(func) \ + if (uMsg == WM_NCCALCSIZE) { \ + SetMsgHandled(TRUE); \ + lResult = func((BOOL)wParam, lParam); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// UINT OnNcHitTest(gfx::Point point) +#define CR_MSG_WM_NCHITTEST(func) \ + if (uMsg == WM_NCHITTEST) { \ + SetMsgHandled(TRUE); \ + lResult = (LRESULT)func( \ + gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnNcPaint(CRgn rgn) +#define CR_MSG_WM_NCPAINT(func) \ + if (uMsg == WM_NCPAINT) { \ + SetMsgHandled(TRUE); \ + func((HRGN)wParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// BOOL OnNcActivate(BOOL bActive) +#define CR_MSG_WM_NCACTIVATE(func) \ + if (uMsg == WM_NCACTIVATE) { \ + SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((BOOL)wParam); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// UINT OnGetDlgCode(LPMSG lpMsg) +#define CR_MSG_WM_GETDLGCODE(func) \ + if (uMsg == WM_GETDLGCODE) { \ + SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((LPMSG)lParam); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnNcMouseMove(UINT nHitTest, gfx::Point point) +#define CR_MSG_WM_NCMOUSEMOVE(func) \ + if (uMsg == WM_NCMOUSEMOVE) { \ + SetMsgHandled(TRUE); \ + func((UINT)wParam, \ + gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnNcLButtonDown(UINT nHitTest, gfx::Point point) +#define CR_MSG_WM_NCLBUTTONDOWN(func) \ + if (uMsg == WM_NCLBUTTONDOWN) { \ + SetMsgHandled(TRUE); \ + func((UINT)wParam, \ + gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnNcLButtonUp(UINT nHitTest, gfx::Point point) +#define CR_MSG_WM_NCLBUTTONUP(func) \ + if (uMsg == WM_NCLBUTTONUP) { \ + SetMsgHandled(TRUE); \ + func((UINT)wParam, \ + gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnNcLButtonDblClk(UINT nHitTest, gfx::Point point) +#define CR_MSG_WM_NCLBUTTONDBLCLK(func) \ + if (uMsg == WM_NCLBUTTONDBLCLK) { \ + SetMsgHandled(TRUE); \ + func((UINT)wParam, \ + gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnNcRButtonDown(UINT nHitTest, gfx::Point point) +#define CR_MSG_WM_NCRBUTTONDOWN(func) \ + if (uMsg == WM_NCRBUTTONDOWN) { \ + SetMsgHandled(TRUE); \ + func((UINT)wParam, \ + gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnNcRButtonUp(UINT nHitTest, gfx::Point point) +#define CR_MSG_WM_NCRBUTTONUP(func) \ + if (uMsg == WM_NCRBUTTONUP) { \ + SetMsgHandled(TRUE); \ + func((UINT)wParam, \ + gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnNcRButtonDblClk(UINT nHitTest, CPoint point) +#define CR_MSG_WM_NCRBUTTONDBLCLK(func) \ + if (uMsg == WM_NCRBUTTONDBLCLK) { \ + SetMsgHandled(TRUE); \ + func((UINT)wParam, \ + gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnNcMButtonDown(UINT nHitTest, CPoint point) +#define CR_MSG_WM_NCMBUTTONDOWN(func) \ + if (uMsg == WM_NCMBUTTONDOWN) { \ + SetMsgHandled(TRUE); \ + func((UINT)wParam, \ + gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnNcMButtonUp(UINT nHitTest, CPoint point) +#define CR_MSG_WM_NCMBUTTONUP(func) \ + if (uMsg == WM_NCMBUTTONUP) { \ + SetMsgHandled(TRUE); \ + func((UINT)wParam, \ + gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnNcMButtonDblClk(UINT nHitTest, CPoint point) +#define CR_MSG_WM_NCMBUTTONDBLCLK(func) \ + if (uMsg == WM_NCMBUTTONDBLCLK) { \ + SetMsgHandled(TRUE); \ + func((UINT)wParam, \ + gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) +#define CR_MSG_WM_KEYDOWN(func) \ + if (uMsg == WM_KEYDOWN) { \ + SetMsgHandled(TRUE); \ + func((TCHAR)wParam, \ + (UINT)lParam & 0xFFFF, \ + (UINT)((lParam & 0xFFFF0000) >> 16)); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags) +#define CR_MSG_WM_KEYUP(func) \ + if (uMsg == WM_KEYUP) { \ + SetMsgHandled(TRUE); \ + func((TCHAR)wParam, \ + (UINT)lParam & 0xFFFF, \ + (UINT)((lParam & 0xFFFF0000) >> 16)); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) +#define CR_MSG_WM_CHAR(func) \ + if (uMsg == WM_CHAR) { \ + SetMsgHandled(TRUE); \ + func((TCHAR)wParam, \ + (UINT)lParam & 0xFFFF, \ + (UINT)((lParam & 0xFFFF0000) >> 16)); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnDeadChar(UINT nChar, UINT nRepCnt, UINT nFlags) +#define CR_MSG_WM_DEADCHAR(func) \ + if (uMsg == WM_DEADCHAR) { \ + SetMsgHandled(TRUE); \ + func((TCHAR)wParam, \ + (UINT)lParam & 0xFFFF, \ + (UINT)((lParam & 0xFFFF0000) >> 16)); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnSysKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) +#define CR_MSG_WM_SYSKEYDOWN(func) \ + if (uMsg == WM_SYSKEYDOWN) { \ + SetMsgHandled(TRUE); \ + func((TCHAR)wParam, \ + (UINT)lParam & 0xFFFF, \ + (UINT)((lParam & 0xFFFF0000) >> 16)); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnSysKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags) +#define CR_MSG_WM_SYSKEYUP(func) \ + if (uMsg == WM_SYSKEYUP) { \ + SetMsgHandled(TRUE); \ + func((TCHAR)wParam, \ + (UINT)lParam & 0xFFFF, \ + (UINT)((lParam & 0xFFFF0000) >> 16)); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnSysChar(UINT nChar, UINT nRepCnt, UINT nFlags) +#define CR_MSG_WM_SYSCHAR(func) \ + if (uMsg == WM_SYSCHAR) { \ + SetMsgHandled(TRUE); \ + func((TCHAR)wParam, \ + (UINT)lParam & 0xFFFF, \ + (UINT)((lParam & 0xFFFF0000) >> 16)); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnSysDeadChar(UINT nChar, UINT nRepCnt, UINT nFlags) +#define CR_MSG_WM_SYSDEADCHAR(func) \ + if (uMsg == WM_SYSDEADCHAR) { \ + SetMsgHandled(TRUE); \ + func((TCHAR)wParam, \ + (UINT)lParam & 0xFFFF, \ + (UINT)((lParam & 0xFFFF0000) >> 16)); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnSysCommand(UINT nID, LPARAM lParam) +#define CR_MSG_WM_SYSCOMMAND(func) \ + if (uMsg == WM_SYSCOMMAND) { \ + SetMsgHandled(TRUE); \ + func((UINT)wParam, \ + gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnTCard(UINT idAction, DWORD dwActionData) +#define CR_MSG_WM_TCARD(func) \ + if (uMsg == WM_TCARD) { \ + SetMsgHandled(TRUE); \ + func((UINT)wParam, (DWORD)lParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnTimer(UINT_PTR nIDEvent) +#define CR_MSG_WM_TIMER(func) \ + if (uMsg == WM_TIMER) { \ + SetMsgHandled(TRUE); \ + func((UINT_PTR)wParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar pScrollBar) +#define CR_MSG_WM_HSCROLL(func) \ + if (uMsg == WM_HSCROLL) { \ + SetMsgHandled(TRUE); \ + func((int)LOWORD(wParam), (short)HIWORD(wParam), (HWND)lParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnVScroll(UINT nSBCode, UINT nPos, CScrollBar pScrollBar) +#define CR_MSG_WM_VSCROLL(func) \ + if (uMsg == WM_VSCROLL) { \ + SetMsgHandled(TRUE); \ + func((int)LOWORD(wParam), (short)HIWORD(wParam), (HWND)lParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnInitMenu(CMenu menu) +#define CR_MSG_WM_INITMENU(func) \ + if (uMsg == WM_INITMENU) { \ + SetMsgHandled(TRUE); \ + func((HMENU)wParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnInitMenuPopup(CMenu menuPopup, UINT nIndex, BOOL bSysMenu) +#define CR_MSG_WM_INITMENUPOPUP(func) \ + if (uMsg == WM_INITMENUPOPUP) { \ + SetMsgHandled(TRUE); \ + func((HMENU)wParam, (UINT)LOWORD(lParam), (BOOL)HIWORD(lParam)); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnMenuSelect(UINT nItemID, UINT nFlags, CMenu menu) +#define CR_MSG_WM_MENUSELECT(func) \ + if (uMsg == WM_MENUSELECT) { \ + SetMsgHandled(TRUE); \ + func((UINT)LOWORD(wParam), (UINT)HIWORD(wParam), (HMENU)lParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// LRESULT OnMenuChar(UINT nChar, UINT nFlags, CMenu menu) +#define CR_MSG_WM_MENUCHAR(func) \ + if (uMsg == WM_MENUCHAR) { \ + SetMsgHandled(TRUE); \ + lResult = \ + func((TCHAR)LOWORD(wParam), (UINT)HIWORD(wParam), (HMENU)lParam); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// LRESULT OnNotify(int idCtrl, LPNMHDR pnmh) +#define CR_MSG_WM_NOTIFY(func) \ + if (uMsg == WM_NOTIFY) { \ + SetMsgHandled(TRUE); \ + lResult = func((int)wParam, (LPNMHDR)lParam); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnEnterIdle(UINT nWhy, CWindow wndWho) +#define CR_MSG_WM_ENTERIDLE(func) \ + if (uMsg == WM_ENTERIDLE) { \ + SetMsgHandled(TRUE); \ + func((UINT)wParam, (HWND)lParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnMouseMove(UINT nFlags, CPoint point) +#define CR_MSG_WM_MOUSEMOVE(func) \ + if (uMsg == WM_MOUSEMOVE) { \ + SetMsgHandled(TRUE); \ + func((UINT)wParam, \ + gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// BOOL OnMouseWheel(UINT nFlags, short zDelta, CPoint pt) +#define CR_MSG_WM_MOUSEWHEEL(func) \ + if (uMsg == WM_MOUSEWHEEL) { \ + SetMsgHandled(TRUE); \ + lResult = (LRESULT)func( \ + (UINT)LOWORD(wParam), \ + (short)HIWORD(wParam), \ + gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnLButtonDown(UINT nFlags, CPoint point) +#define CR_MSG_WM_LBUTTONDOWN(func) \ + if (uMsg == WM_LBUTTONDOWN) { \ + SetMsgHandled(TRUE); \ + func((UINT)wParam, \ + gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnLButtonUp(UINT nFlags, CPoint point) +#define CR_MSG_WM_LBUTTONUP(func) \ + if (uMsg == WM_LBUTTONUP) { \ + SetMsgHandled(TRUE); \ + func((UINT)wParam, \ + gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnLButtonDblClk(UINT nFlags, CPoint point) +#define CR_MSG_WM_LBUTTONDBLCLK(func) \ + if (uMsg == WM_LBUTTONDBLCLK) { \ + SetMsgHandled(TRUE); \ + func((UINT)wParam, \ + gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnRButtonDown(UINT nFlags, CPoint point) +#define CR_MSG_WM_RBUTTONDOWN(func) \ + if (uMsg == WM_RBUTTONDOWN) { \ + SetMsgHandled(TRUE); \ + func((UINT)wParam, \ + gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnRButtonUp(UINT nFlags, CPoint point) +#define CR_MSG_WM_RBUTTONUP(func) \ + if (uMsg == WM_RBUTTONUP) { \ + SetMsgHandled(TRUE); \ + func((UINT)wParam, \ + gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnRButtonDblClk(UINT nFlags, CPoint point) +#define CR_MSG_WM_RBUTTONDBLCLK(func) \ + if (uMsg == WM_RBUTTONDBLCLK) { \ + SetMsgHandled(TRUE); \ + func((UINT)wParam, \ + gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnMButtonDown(UINT nFlags, CPoint point) +#define CR_MSG_WM_MBUTTONDOWN(func) \ + if (uMsg == WM_MBUTTONDOWN) { \ + SetMsgHandled(TRUE); \ + func((UINT)wParam, \ + gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnMButtonUp(UINT nFlags, CPoint point) +#define CR_MSG_WM_MBUTTONUP(func) \ + if (uMsg == WM_MBUTTONUP) { \ + SetMsgHandled(TRUE); \ + func((UINT)wParam, \ + gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnMButtonDblClk(UINT nFlags, CPoint point) +#define CR_MSG_WM_MBUTTONDBLCLK(func) \ + if (uMsg == WM_MBUTTONDBLCLK) { \ + SetMsgHandled(TRUE); \ + func((UINT)wParam, \ + gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnParentNotify(UINT message, UINT nChildID, LPARAM lParam) +#define CR_MSG_WM_PARENTNOTIFY(func) \ + if (uMsg == WM_PARENTNOTIFY) { \ + SetMsgHandled(TRUE); \ + func((UINT)LOWORD(wParam), (UINT)HIWORD(wParam), lParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnMDIActivate(CWindow wndActivate, CWindow wndDeactivate) +#define CR_MSG_WM_MDIACTIVATE(func) \ + if (uMsg == WM_MDIACTIVATE) { \ + SetMsgHandled(TRUE); \ + func((HWND)wParam, (HWND)lParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnRenderFormat(UINT nFormat) +#define CR_MSG_WM_RENDERFORMAT(func) \ + if (uMsg == WM_RENDERFORMAT) { \ + SetMsgHandled(TRUE); \ + func((UINT)wParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnRenderAllFormats() +#define CR_MSG_WM_RENDERALLFORMATS(func) \ + if (uMsg == WM_RENDERALLFORMATS) { \ + SetMsgHandled(TRUE); \ + func(); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnDestroyClipboard() +#define CR_MSG_WM_DESTROYCLIPBOARD(func) \ + if (uMsg == WM_DESTROYCLIPBOARD) { \ + SetMsgHandled(TRUE); \ + func(); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnDrawClipboard() +#define CR_MSG_WM_DRAWCLIPBOARD(func) \ + if (uMsg == WM_DRAWCLIPBOARD) { \ + SetMsgHandled(TRUE); \ + func(); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnPaintClipboard(CWindow wndViewer, const LPPAINTSTRUCT lpPaintStruct) +#define CR_MSG_WM_PAINTCLIPBOARD(func) \ + if (uMsg == WM_PAINTCLIPBOARD) { \ + SetMsgHandled(TRUE); \ + func((HWND)wParam, (const LPPAINTSTRUCT)::GlobalLock((HGLOBAL)lParam)); \ + ::GlobalUnlock((HGLOBAL)lParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnVScrollClipboard(CWindow wndViewer, UINT nSBCode, UINT nPos) +#define CR_MSG_WM_VSCROLLCLIPBOARD(func) \ + if (uMsg == WM_VSCROLLCLIPBOARD) { \ + SetMsgHandled(TRUE); \ + func((HWND)wParam, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam)); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnContextMenu(CWindow wnd, CPoint point) +#define CR_MSG_WM_CONTEXTMENU(func) \ + if (uMsg == WM_CONTEXTMENU) { \ + SetMsgHandled(TRUE); \ + func((HWND)wParam, \ + gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnSizeClipboard(CWindow wndViewer, const LPRECT lpRect) +#define CR_MSG_WM_SIZECLIPBOARD(func) \ + if (uMsg == WM_SIZECLIPBOARD) { \ + SetMsgHandled(TRUE); \ + func((HWND)wParam, (const LPRECT)::GlobalLock((HGLOBAL)lParam)); \ + ::GlobalUnlock((HGLOBAL)lParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnAskCbFormatName(UINT nMaxCount, LPTSTR lpszString) +#define CR_MSG_WM_ASKCBFORMATNAME(func) \ + if (uMsg == WM_ASKCBFORMATNAME) { \ + SetMsgHandled(TRUE); \ + func((DWORD)wParam, (LPTSTR)lParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnChangeCbChain(CWindow wndRemove, CWindow wndAfter) +#define CR_MSG_WM_CHANGECBCHAIN(func) \ + if (uMsg == WM_CHANGECBCHAIN) { \ + SetMsgHandled(TRUE); \ + func((HWND)wParam, (HWND)lParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnHScrollClipboard(CWindow wndViewer, UINT nSBCode, UINT nPos) +#define CR_MSG_WM_HSCROLLCLIPBOARD(func) \ + if (uMsg == WM_HSCROLLCLIPBOARD) { \ + SetMsgHandled(TRUE); \ + func((HWND)wParam, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam)); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// BOOL OnQueryNewPalette() +#define CR_MSG_WM_QUERYNEWPALETTE(func) \ + if (uMsg == WM_QUERYNEWPALETTE) { \ + SetMsgHandled(TRUE); \ + lResult = (LRESULT)func(); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnPaletteChanged(CWindow wndFocus) +#define CR_MSG_WM_PALETTECHANGED(func) \ + if (uMsg == WM_PALETTECHANGED) { \ + SetMsgHandled(TRUE); \ + func((HWND)wParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnPaletteIsChanging(CWindow wndPalChg) +#define CR_MSG_WM_PALETTEISCHANGING(func) \ + if (uMsg == WM_PALETTEISCHANGING) { \ + SetMsgHandled(TRUE); \ + func((HWND)wParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnDropFiles(HDROP hDropInfo) +#define CR_MSG_WM_DROPFILES(func) \ + if (uMsg == WM_DROPFILES) { \ + SetMsgHandled(TRUE); \ + func((HDROP)wParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnWindowPosChanging(LPWINDOWPOS lpWndPos) +#define CR_MSG_WM_WINDOWPOSCHANGING(func) \ + if (uMsg == WM_WINDOWPOSCHANGING) { \ + SetMsgHandled(TRUE); \ + func((LPWINDOWPOS)lParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnWindowPosChanged(LPWINDOWPOS lpWndPos) +#define CR_MSG_WM_WINDOWPOSCHANGED(func) \ + if (uMsg == WM_WINDOWPOSCHANGED) { \ + SetMsgHandled(TRUE); \ + func((LPWINDOWPOS)lParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnExitMenuLoop(BOOL fIsTrackPopupMenu) +#define CR_MSG_WM_EXITMENULOOP(func) \ + if (uMsg == WM_EXITMENULOOP) { \ + SetMsgHandled(TRUE); \ + func((BOOL)wParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnEnterMenuLoop(BOOL fIsTrackPopupMenu) +#define CR_MSG_WM_ENTERMENULOOP(func) \ + if (uMsg == WM_ENTERMENULOOP) { \ + SetMsgHandled(TRUE); \ + func((BOOL)wParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnStyleChanged(int nStyleType, LPSTYLESTRUCT lpStyleStruct) +#define CR_MSG_WM_STYLECHANGED(func) \ + if (uMsg == WM_STYLECHANGED) { \ + SetMsgHandled(TRUE); \ + func((UINT)wParam, (LPSTYLESTRUCT)lParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnStyleChanging(int nStyleType, LPSTYLESTRUCT lpStyleStruct) +#define CR_MSG_WM_STYLECHANGING(func) \ + if (uMsg == WM_STYLECHANGING) { \ + SetMsgHandled(TRUE); \ + func((UINT)wParam, (LPSTYLESTRUCT)lParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnSizing(UINT fwSide, LPRECT pRect) +#define CR_MSG_WM_SIZING(func) \ + if (uMsg == WM_SIZING) { \ + SetMsgHandled(TRUE); \ + func((UINT)wParam, (LPRECT)lParam); \ + lResult = TRUE; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnMoving(UINT fwSide, LPRECT pRect) +#define CR_MSG_WM_MOVING(func) \ + if (uMsg == WM_MOVING) { \ + SetMsgHandled(TRUE); \ + func((UINT)wParam, (LPRECT)lParam); \ + lResult = TRUE; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnCaptureChanged(CWindow wnd) +#define CR_MSG_WM_CAPTURECHANGED(func) \ + if (uMsg == WM_CAPTURECHANGED) { \ + SetMsgHandled(TRUE); \ + func((HWND)lParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// BOOL OnDeviceChange(UINT nEventType, DWORD dwData) +#define CR_MSG_WM_DEVICECHANGE(func) \ + if (uMsg == WM_DEVICECHANGE) { \ + SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((UINT)wParam, (DWORD)lParam); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnCommand(UINT uNotifyCode, int nID, CWindow wndCtl) +#define CR_MSG_WM_COMMAND(func) \ + if (uMsg == WM_COMMAND) { \ + SetMsgHandled(TRUE); \ + func((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnDisplayChange(UINT uBitsPerPixel, gfx::Size sizeScreen) +#define CR_MSG_WM_DISPLAYCHANGE(func) \ + if (uMsg == WM_DISPLAYCHANGE) { \ + SetMsgHandled(TRUE); \ + func((UINT)wParam, \ + gfx::Size(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnEnterSizeMove() +#define CR_MSG_WM_ENTERSIZEMOVE(func) \ + if (uMsg == WM_ENTERSIZEMOVE) { \ + SetMsgHandled(TRUE); \ + func(); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnExitSizeMove() +#define CR_MSG_WM_EXITSIZEMOVE(func) \ + if (uMsg == WM_EXITSIZEMOVE) { \ + SetMsgHandled(TRUE); \ + func(); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// HFONT OnGetFont() +#define CR_MSG_WM_GETFONT(func) \ + if (uMsg == WM_GETFONT) { \ + SetMsgHandled(TRUE); \ + lResult = (LRESULT)func(); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// LRESULT OnGetHotKey() +#define CR_MSG_WM_GETHOTKEY(func) \ + if (uMsg == WM_GETHOTKEY) { \ + SetMsgHandled(TRUE); \ + lResult = func(); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// HICON OnGetIcon() +#define CR_MSG_WM_GETICON(func) \ + if (uMsg == WM_GETICON) { \ + SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((UINT)wParam); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// int OnGetText(int cchTextMax, LPTSTR lpszText) +#define CR_MSG_WM_GETTEXT(func) \ + if (uMsg == WM_GETTEXT) { \ + SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((int)wParam, (LPTSTR)lParam); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// int OnGetTextLength() +#define CR_MSG_WM_GETTEXTLENGTH(func) \ + if (uMsg == WM_GETTEXTLENGTH) { \ + SetMsgHandled(TRUE); \ + lResult = (LRESULT)func(); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnHelp(LPHELPINFO lpHelpInfo) +#define CR_MSG_WM_HELP(func) \ + if (uMsg == WM_HELP) { \ + SetMsgHandled(TRUE); \ + func((LPHELPINFO)lParam); \ + lResult = TRUE; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnHotKey(int nHotKeyID, UINT uModifiers, UINT uVirtKey) +#define CR_MSG_WM_HOTKEY(func) \ + if (uMsg == WM_HOTKEY) { \ + SetMsgHandled(TRUE); \ + func((int)wParam, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam)); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnInputLangChange(DWORD dwCharSet, HKL hKbdLayout) +#define CR_MSG_WM_INPUTLANGCHANGE(func) \ + if (uMsg == WM_INPUTLANGCHANGE) { \ + SetMsgHandled(TRUE); \ + func((DWORD)wParam, (HKL)lParam); \ + lResult = TRUE; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnInputLangChangeRequest(BOOL bSysCharSet, HKL hKbdLayout) +#define CR_MSG_WM_INPUTLANGCHANGEREQUEST(func) \ + if (uMsg == WM_INPUTLANGCHANGEREQUEST) { \ + SetMsgHandled(TRUE); \ + func((BOOL)wParam, (HKL)lParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnNextDlgCtl(BOOL bHandle, WPARAM wCtlFocus) +#define CR_MSG_WM_NEXTDLGCTL(func) \ + if (uMsg == WM_NEXTDLGCTL) { \ + SetMsgHandled(TRUE); \ + func((BOOL)LOWORD(lParam), wParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnNextMenu(int nVirtKey, LPMDINEXTMENU lpMdiNextMenu) +#define CR_MSG_WM_NEXTMENU(func) \ + if (uMsg == WM_NEXTMENU) { \ + SetMsgHandled(TRUE); \ + func((int)wParam, (LPMDINEXTMENU)lParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// int OnNotifyFormat(CWindow wndFrom, int nCommand) +#define CR_MSG_WM_NOTIFYFORMAT(func) \ + if (uMsg == WM_NOTIFYFORMAT) { \ + SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((HWND)wParam, (int)lParam); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// BOOL OnPowerBroadcast(DWORD dwPowerEvent, DWORD dwData) +#define CR_MSG_WM_POWERBROADCAST(func) \ + if (uMsg == WM_POWERBROADCAST) { \ + SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((DWORD)wParam, (DWORD)lParam); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnPrint(CDCHandle dc, UINT uFlags) +#define CR_MSG_WM_PRINT(func) \ + if (uMsg == WM_PRINT) { \ + SetMsgHandled(TRUE); \ + func((HDC)wParam, (UINT)lParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnPrintClient(CDCHandle dc, UINT uFlags) +#define CR_MSG_WM_PRINTCLIENT(func) \ + if (uMsg == WM_PRINTCLIENT) { \ + SetMsgHandled(TRUE); \ + func((HDC)wParam, (UINT)lParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnRasDialEvent(RASCONNSTATE rasconnstate, DWORD dwError) +#define CR_MSG_WM_RASDIALEVENT(func) \ + if (uMsg == WM_RASDIALEVENT) { \ + SetMsgHandled(TRUE); \ + func((RASCONNSTATE)wParam, (DWORD)lParam); \ + lResult = TRUE; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnSetFont(CFont font, BOOL bRedraw) +#define CR_MSG_WM_SETFONT(func) \ + if (uMsg == WM_SETFONT) { \ + SetMsgHandled(TRUE); \ + func((HFONT)wParam, (BOOL)LOWORD(lParam)); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// int OnSetHotKey(int nVirtKey, UINT uFlags) +#define CR_MSG_WM_SETHOTKEY(func) \ + if (uMsg == WM_SETHOTKEY) { \ + SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((int)LOBYTE(LOWORD(wParam)), \ + (UINT)HIBYTE(LOWORD(wParam))); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// HICON OnSetIcon(UINT uType, HICON hIcon) +#define CR_MSG_WM_SETICON(func) \ + if (uMsg == WM_SETICON) { \ + SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((UINT)wParam, (HICON)lParam); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnSetRedraw(BOOL bRedraw) +#define CR_MSG_WM_SETREDRAW(func) \ + if (uMsg == WM_SETREDRAW) { \ + SetMsgHandled(TRUE); \ + func((BOOL)wParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// int OnSetText(LPCTSTR lpstrText) +#define CR_MSG_WM_SETTEXT(func) \ + if (uMsg == WM_SETTEXT) { \ + SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((LPCTSTR)lParam); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnUserChanged() +#define CR_MSG_WM_USERCHANGED(func) \ + if (uMsg == WM_USERCHANGED) { \ + SetMsgHandled(TRUE); \ + func(); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +/////////////////////////////////////////////////////////////////////////////// +// New NT4 & NT5 messages + +#if (_WIN32_WINNT >= 0x0400) + +// void OnMouseHover(WPARAM wParam, CPoint ptPos) +#define CR_MSG_WM_MOUSEHOVER(func) \ + if (uMsg == WM_MOUSEHOVER) { \ + SetMsgHandled(TRUE); \ + func(wParam, \ + gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnMouseLeave() +#define CR_MSG_WM_MOUSELEAVE(func) \ + if (uMsg == WM_MOUSELEAVE) { \ + SetMsgHandled(TRUE); \ + func(); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +#endif /* _WIN32_WINNT >= 0x0400 */ + +#if (WINVER >= 0x0500) + +// void OnMenuRButtonUp(WPARAM wParam, CMenu menu) +#define CR_MSG_WM_MENURBUTTONUP(func) \ + if (uMsg == WM_MENURBUTTONUP) { \ + SetMsgHandled(TRUE); \ + func(wParam, (HMENU)lParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// LRESULT OnMenuDrag(WPARAM wParam, CMenu menu) +#define CR_MSG_WM_MENUDRAG(func) \ + if (uMsg == WM_MENUDRAG) { \ + SetMsgHandled(TRUE); \ + lResult = func(wParam, (HMENU)lParam); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// LRESULT OnMenuGetObject(PMENUGETOBJECTINFO info) +#define CR_MSG_WM_MENUGETOBJECT(func) \ + if (uMsg == WM_MENUGETOBJECT) { \ + SetMsgHandled(TRUE); \ + lResult = func((PMENUGETOBJECTINFO)lParam); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnUnInitMenuPopup(UINT nID, CMenu menu) +#define CR_MSG_WM_UNINITMENUPOPUP(func) \ + if (uMsg == WM_UNINITMENUPOPUP) { \ + SetMsgHandled(TRUE); \ + func((UINT)HIWORD(lParam), (HMENU)wParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnMenuCommand(WPARAM nIndex, CMenu menu) +#define CR_MSG_WM_MENUCOMMAND(func) \ + if (uMsg == WM_MENUCOMMAND) { \ + SetMsgHandled(TRUE); \ + func(wParam, (HMENU)lParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +#endif /* WINVER >= 0x0500 */ + +#if (_WIN32_WINNT >= 0x0500) + +// BOOL OnAppCommand(CWindow wndFocus, short cmd, WORD uDevice, int dwKeys) +#define CR_MSG_WM_APPCOMMAND(func) \ + if (uMsg == WM_APPCOMMAND) { \ + SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((HWND)wParam, \ + GET_APPCOMMAND_LPARAM(lParam), \ + GET_DEVICE_LPARAM(lParam), \ + GET_KEYSTATE_LPARAM(lParam)); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnNCXButtonDown(int fwButton, short nHittest, CPoint ptPos) +#define CR_MSG_WM_NCXBUTTONDOWN(func) \ + if (uMsg == WM_NCXBUTTONDOWN) { \ + SetMsgHandled(TRUE); \ + func(GET_XBUTTON_WPARAM(wParam), \ + GET_NCHITTEST_WPARAM(wParam), \ + gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnNCXButtonUp(int fwButton, short nHittest, CPoint ptPos) +#define CR_MSG_WM_NCXBUTTONUP(func) \ + if (uMsg == WM_NCXBUTTONUP) { \ + SetMsgHandled(TRUE); \ + func(GET_XBUTTON_WPARAM(wParam), \ + GET_NCHITTEST_WPARAM(wParam), \ + gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnNCXButtonDblClk(int fwButton, short nHittest, CPoint ptPos) +#define CR_MSG_WM_NCXBUTTONDBLCLK(func) \ + if (uMsg == WM_NCXBUTTONDBLCLK) { \ + SetMsgHandled(TRUE); \ + func(GET_XBUTTON_WPARAM(wParam), \ + GET_NCHITTEST_WPARAM(wParam), \ + gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnXButtonDown(int fwButton, int dwKeys, CPoint ptPos) +#define CR_MSG_WM_XBUTTONDOWN(func) \ + if (uMsg == WM_XBUTTONDOWN) { \ + SetMsgHandled(TRUE); \ + func(GET_XBUTTON_WPARAM(wParam), \ + GET_KEYSTATE_WPARAM(wParam), \ + gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnXButtonUp(int fwButton, int dwKeys, CPoint ptPos) +#define CR_MSG_WM_XBUTTONUP(func) \ + if (uMsg == WM_XBUTTONUP) { \ + SetMsgHandled(TRUE); \ + func(GET_XBUTTON_WPARAM(wParam), \ + GET_KEYSTATE_WPARAM(wParam), \ + gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnXButtonDblClk(int fwButton, int dwKeys, CPoint ptPos) +#define CR_MSG_WM_XBUTTONDBLCLK(func) \ + if (uMsg == WM_XBUTTONDBLCLK) { \ + SetMsgHandled(TRUE); \ + func(GET_XBUTTON_WPARAM(wParam), \ + GET_KEYSTATE_WPARAM(wParam), \ + gfx::Point(CR_GET_X_LPARAM(lParam), CR_GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnChangeUIState(WORD nAction, WORD nState) +#define CR_MSG_WM_CHANGEUISTATE(func) \ + if (uMsg == WM_CHANGEUISTATE) { \ + SetMsgHandled(TRUE); \ + func(LOWORD(wParam), HIWORD(wParam)); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnUpdateUIState(WORD nAction, WORD nState) +#define CR_MSG_WM_UPDATEUISTATE(func) \ + if (uMsg == WM_UPDATEUISTATE) { \ + SetMsgHandled(TRUE); \ + func(LOWORD(wParam), HIWORD(wParam)); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// LRESULT OnQueryUIState() +#define CR_MSG_WM_QUERYUISTATE(func) \ + if (uMsg == WM_QUERYUISTATE) { \ + SetMsgHandled(TRUE); \ + lResult = func(); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +#endif // (_WIN32_WINNT >= 0x0500) + +#if (_WIN32_WINNT >= 0x0501) + +// void OnInput(WPARAM RawInputCode, HRAWINPUT hRawInput) +#define CR_MSG_WM_INPUT(func) \ + if (uMsg == WM_INPUT) { \ + SetMsgHandled(TRUE); \ + func(GET_RAWINPUT_CODE_WPARAM(wParam), (HRAWINPUT)lParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnUniChar(TCHAR nChar, UINT nRepCnt, UINT nFlags) +#define CR_MSG_WM_UNICHAR(func) \ + if (uMsg == WM_UNICHAR) { \ + SetMsgHandled(TRUE); \ + func((TCHAR)wParam, \ + (UINT)lParam & 0xFFFF, \ + (UINT)((lParam & 0xFFFF0000) >> 16)); \ + if (IsMsgHandled()) { \ + lResult = (wParam == UNICODE_NOCHAR) ? TRUE : FALSE; \ + return TRUE; \ + } \ + } + +// void OnWTSSessionChange(WPARAM nStatusCode, PWTSSESSION_NOTIFICATION +// nSessionID) +#define CR_MSG_WM_WTSSESSION_CHANGE(func) \ + if (uMsg == WM_WTSSESSION_CHANGE) { \ + SetMsgHandled(TRUE); \ + func(wParam, (PWTSSESSION_NOTIFICATION)lParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// OnThemeChanged() +#define CR_MSG_WM_THEMECHANGED(func) \ + if (uMsg == WM_THEMECHANGED) { \ + SetMsgHandled(TRUE); \ + func(); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +#endif /* _WIN32_WINNT >= 0x0501 */ + +/////////////////////////////////////////////////////////////////////////////// +// ATL defined messages + +// BOOL OnForwardMsg(LPMSG Msg, DWORD nUserData) +#define CR_MSG_WM_FORWARDMSG(func) \ + if (uMsg == WM_FORWARDMSG) { \ + SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((LPMSG)lParam, (DWORD)wParam); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +/////////////////////////////////////////////////////////////////////////////// +// Dialog specific messages + +// LRESULT OnDMGetDefID() +#define MSG_DM_GETDEFID(func) \ + if (uMsg == DM_GETDEFID) { \ + SetMsgHandled(TRUE); \ + lResult = func(); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnDMSetDefID(UINT DefID) +#define MSG_DM_SETDEFID(func) \ + if (uMsg == DM_SETDEFID) { \ + SetMsgHandled(TRUE); \ + func((UINT)wParam); \ + lResult = TRUE; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnDMReposition() +#define MSG_DM_REPOSITION(func) \ + if (uMsg == DM_REPOSITION) { \ + SetMsgHandled(TRUE); \ + func(); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +/////////////////////////////////////////////////////////////////////////////// +// Reflected messages + +// void OnReflectedCommand(UINT uNotifyCode, int nID, CWindow wndCtl) +#define MSG_OCM_COMMAND(func) \ + if (uMsg == OCM_COMMAND) { \ + SetMsgHandled(TRUE); \ + func((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// LRESULT OnReflectedNotify(int idCtrl, LPNMHDR pnmh) +#define MSG_OCM_NOTIFY(func) \ + if (uMsg == OCM_NOTIFY) { \ + SetMsgHandled(TRUE); \ + lResult = func((int)wParam, (LPNMHDR)lParam); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnReflectedParentNotify(UINT message, UINT nChildID, LPARAM lParam) +#define MSG_OCM_PARENTNOTIFY(func) \ + if (uMsg == OCM_PARENTNOTIFY) { \ + SetMsgHandled(TRUE); \ + func((UINT)LOWORD(wParam), (UINT)HIWORD(wParam), lParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnReflectedDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct) +#define MSG_OCM_DRAWITEM(func) \ + if (uMsg == OCM_DRAWITEM) { \ + SetMsgHandled(TRUE); \ + func((UINT)wParam, (LPDRAWITEMSTRUCT)lParam); \ + lResult = TRUE; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnReflectedMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT +// lpMeasureItemStruct) +#define MSG_OCM_MEASUREITEM(func) \ + if (uMsg == OCM_MEASUREITEM) { \ + SetMsgHandled(TRUE); \ + func((UINT)wParam, (LPMEASUREITEMSTRUCT)lParam); \ + lResult = TRUE; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// int OnReflectedCompareItem(int nIDCtl, LPCOMPAREITEMSTRUCT +// lpCompareItemStruct) +#define MSG_OCM_COMPAREITEM(func) \ + if (uMsg == OCM_COMPAREITEM) { \ + SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((UINT)wParam, (LPCOMPAREITEMSTRUCT)lParam); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnReflectedDeleteItem(int nIDCtl, LPDELETEITEMSTRUCT lpDeleteItemStruct) +#define MSG_OCM_DELETEITEM(func) \ + if (uMsg == OCM_DELETEITEM) { \ + SetMsgHandled(TRUE); \ + func((UINT)wParam, (LPDELETEITEMSTRUCT)lParam); \ + lResult = TRUE; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// int OnReflectedVKeyToItem(UINT nKey, UINT nIndex, CListBox listBox) +#define MSG_OCM_VKEYTOITEM(func) \ + if (uMsg == OCM_VKEYTOITEM) { \ + SetMsgHandled(TRUE); \ + lResult = (LRESULT)func( \ + (UINT)LOWORD(wParam), (UINT)HIWORD(wParam), (HWND)lParam); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// int OnReflectedCharToItem(UINT nChar, UINT nIndex, CListBox listBox) +#define MSG_OCM_CHARTOITEM(func) \ + if (uMsg == OCM_CHARTOITEM) { \ + SetMsgHandled(TRUE); \ + lResult = (LRESULT)func( \ + (UINT)LOWORD(wParam), (UINT)HIWORD(wParam), (HWND)lParam); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnReflectedHScroll(UINT nSBCode, UINT nPos, CScrollBar pScrollBar) +#define MSG_OCM_HSCROLL(func) \ + if (uMsg == OCM_HSCROLL) { \ + SetMsgHandled(TRUE); \ + func((int)LOWORD(wParam), (short)HIWORD(wParam), (HWND)lParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnReflectedVScroll(UINT nSBCode, UINT nPos, CScrollBar pScrollBar) +#define MSG_OCM_VSCROLL(func) \ + if (uMsg == OCM_VSCROLL) { \ + SetMsgHandled(TRUE); \ + func((int)LOWORD(wParam), (short)HIWORD(wParam), (HWND)lParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// HBRUSH OnReflectedCtlColorEdit(CDCHandle dc, CEdit edit) +#define MSG_OCM_CTLCOLOREDIT(func) \ + if (uMsg == OCM_CTLCOLOREDIT) { \ + SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// HBRUSH OnReflectedCtlColorListBox(CDCHandle dc, CListBox listBox) +#define MSG_OCM_CTLCOLORLISTBOX(func) \ + if (uMsg == OCM_CTLCOLORLISTBOX) { \ + SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// HBRUSH OnReflectedCtlColorBtn(CDCHandle dc, CButton button) +#define MSG_OCM_CTLCOLORBTN(func) \ + if (uMsg == OCM_CTLCOLORBTN) { \ + SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// HBRUSH OnReflectedCtlColorDlg(CDCHandle dc, CWindow wnd) +#define MSG_OCM_CTLCOLORDLG(func) \ + if (uMsg == OCM_CTLCOLORDLG) { \ + SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// HBRUSH OnReflectedCtlColorScrollBar(CDCHandle dc, CScrollBar scrollBar) +#define MSG_OCM_CTLCOLORSCROLLBAR(func) \ + if (uMsg == OCM_CTLCOLORSCROLLBAR) { \ + SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// HBRUSH OnReflectedCtlColorStatic(CDCHandle dc, CStatic wndStatic) +#define MSG_OCM_CTLCOLORSTATIC(func) \ + if (uMsg == OCM_CTLCOLORSTATIC) { \ + SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +/////////////////////////////////////////////////////////////////////////////// +// Edit specific messages + +// void OnClear() +#define CR_MSG_WM_CLEAR(func) \ + if (uMsg == WM_CLEAR) { \ + SetMsgHandled(TRUE); \ + func(); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnCopy() +#define CR_MSG_WM_COPY(func) \ + if (uMsg == WM_COPY) { \ + SetMsgHandled(TRUE); \ + func(); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnCut() +#define CR_MSG_WM_CUT(func) \ + if (uMsg == WM_CUT) { \ + SetMsgHandled(TRUE); \ + func(); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnPaste() +#define CR_MSG_WM_PASTE(func) \ + if (uMsg == WM_PASTE) { \ + SetMsgHandled(TRUE); \ + func(); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnUndo() +#define CR_MSG_WM_UNDO(func) \ + if (uMsg == WM_UNDO) { \ + SetMsgHandled(TRUE); \ + func(); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +/////////////////////////////////////////////////////////////////////////////// +// Generic message handlers + +// LRESULT OnMessageHandlerEX(UINT uMsg, WPARAM wParam, LPARAM lParam) +#define CR_MESSAGE_HANDLER_EX(msg, func) \ + if (uMsg == msg) { \ + SetMsgHandled(TRUE); \ + lResult = func(uMsg, wParam, lParam); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// LRESULT OnMessageRangeHandlerEX(UINT uMsg, WPARAM wParam, LPARAM lParam) +#define CR_MESSAGE_RANGE_HANDLER_EX(msgFirst, msgLast, func) \ + if (uMsg >= msgFirst && uMsg <= msgLast) { \ + SetMsgHandled(TRUE); \ + lResult = func(uMsg, wParam, lParam); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +/////////////////////////////////////////////////////////////////////////////// +// Commands and notifications + +// void OnCommandHandlerEX(UINT uNotifyCode, int nID, CWindow wndCtl) +#define CR_COMMAND_HANDLER_EX(id, code, func) \ + if (uMsg == WM_COMMAND && code == HIWORD(wParam) && id == LOWORD(wParam)) { \ + SetMsgHandled(TRUE); \ + func((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnCommandIDHandlerEX(UINT uNotifyCode, int nID, CWindow wndCtl) +#define CR_COMMAND_ID_HANDLER_EX(id, func) \ + if (uMsg == WM_COMMAND && id == LOWORD(wParam)) { \ + SetMsgHandled(TRUE); \ + func((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnCommandCodeHandlerEX(UINT uNotifyCode, int nID, CWindow wndCtl) +#define CR_COMMAND_CODE_HANDLER_EX(code, func) \ + if (uMsg == WM_COMMAND && code == HIWORD(wParam)) { \ + SetMsgHandled(TRUE); \ + func((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// LRESULT OnNotifyHandlerEX(LPNMHDR pnmh) +#define CR_NOTIFY_HANDLER_EX(id, cd, func) \ + if (uMsg == WM_NOTIFY && cd == ((LPNMHDR)lParam)->code && \ + id == ((LPNMHDR)lParam)->idFrom) { \ + SetMsgHandled(TRUE); \ + lResult = func((LPNMHDR)lParam); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// LRESULT OnNotifyIDHandlerEX(LPNMHDR pnmh) +#define CR_NOTIFY_ID_HANDLER_EX(id, func) \ + if (uMsg == WM_NOTIFY && id == ((LPNMHDR)lParam)->idFrom) { \ + SetMsgHandled(TRUE); \ + lResult = func((LPNMHDR)lParam); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// LRESULT OnNotifyCodeHandlerEX(LPNMHDR pnmh) +#define CR_NOTIFY_CODE_HANDLER_EX(cd, func) \ + if (uMsg == WM_NOTIFY && cd == ((LPNMHDR)lParam)->code) { \ + SetMsgHandled(TRUE); \ + lResult = func((LPNMHDR)lParam); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnCommandRangeHandlerEX(UINT uNotifyCode, int nID, CWindow wndCtl) +#define CR_COMMAND_RANGE_HANDLER_EX(idFirst, idLast, func) \ + if (uMsg == WM_COMMAND && LOWORD(wParam) >= idFirst && \ + LOWORD(wParam) <= idLast) { \ + SetMsgHandled(TRUE); \ + func((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnCommandRangeCodeHandlerEX(UINT uNotifyCode, int nID, CWindow wndCtl) +#define CR_COMMAND_RANGE_CODE_HANDLER_EX(idFirst, idLast, code, func) \ + if (uMsg == WM_COMMAND && code == HIWORD(wParam) && \ + LOWORD(wParam) >= idFirst && LOWORD(wParam) <= idLast) { \ + SetMsgHandled(TRUE); \ + func((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// LRESULT OnNotifyRangeHandlerEX(LPNMHDR pnmh) +#define CR_NOTIFY_RANGE_HANDLER_EX(idFirst, idLast, func) \ + if (uMsg == WM_NOTIFY && ((LPNMHDR)lParam)->idFrom >= idFirst && \ + ((LPNMHDR)lParam)->idFrom <= idLast) { \ + SetMsgHandled(TRUE); \ + lResult = func((LPNMHDR)lParam); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// LRESULT OnNotifyRangeCodeHandlerEX(LPNMHDR pnmh) +#define CR_NOTIFY_RANGE_CODE_HANDLER_EX(idFirst, idLast, cd, func) \ + if (uMsg == WM_NOTIFY && cd == ((LPNMHDR)lParam)->code && \ + ((LPNMHDR)lParam)->idFrom >= idFirst && \ + ((LPNMHDR)lParam)->idFrom <= idLast) { \ + SetMsgHandled(TRUE); \ + lResult = func((LPNMHDR)lParam); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// LRESULT OnReflectedCommandHandlerEX(UINT uNotifyCode, int nID, CWindow +// wndCtl) +#define CR_REFLECTED_COMMAND_HANDLER_EX(id, code, func) \ + if (uMsg == OCM_COMMAND && code == HIWORD(wParam) && id == LOWORD(wParam)) { \ + SetMsgHandled(TRUE); \ + func((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// LRESULT OnReflectedCommandIDHandlerEX(UINT uNotifyCode, int nID, CWindow +// wndCtl) +#define CR_REFLECTED_COMMAND_ID_HANDLER_EX(id, func) \ + if (uMsg == OCM_COMMAND && id == LOWORD(wParam)) { \ + SetMsgHandled(TRUE); \ + func((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// LRESULT OnReflectedCommandCodeHandlerEX(UINT uNotifyCode, int nID, CWindow +// wndCtl) +#define CR_REFLECTED_COMMAND_CODE_HANDLER_EX(code, func) \ + if (uMsg == OCM_COMMAND && code == HIWORD(wParam)) { \ + SetMsgHandled(TRUE); \ + func((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// LRESULT OnReflectedNotifyHandlerEX(LPNMHDR pnmh) +#define CR_REFLECTED_NOTIFY_HANDLER_EX(id, cd, func) \ + if (uMsg == OCM_NOTIFY && cd == ((LPNMHDR)lParam)->code && \ + id == ((LPNMHDR)lParam)->idFrom) { \ + SetMsgHandled(TRUE); \ + lResult = func((LPNMHDR)lParam); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// LRESULT OnReflectedNotifyIDHandlerEX(LPNMHDR pnmh) +#define CR_REFLECTED_NOTIFY_ID_HANDLER_EX(id, func) \ + if (uMsg == OCM_NOTIFY && id == ((LPNMHDR)lParam)->idFrom) { \ + SetMsgHandled(TRUE); \ + lResult = func((LPNMHDR)lParam); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// LRESULT OnReflectedNotifyCodeHandlerEX(LPNMHDR pnmh) +#define CR_REFLECTED_NOTIFY_CODE_HANDLER_EX(cd, func) \ + if (uMsg == OCM_NOTIFY && cd == ((LPNMHDR)lParam)->code) { \ + SetMsgHandled(TRUE); \ + lResult = func((LPNMHDR)lParam); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnReflectedCommandRangeHandlerEX(UINT uNotifyCode, int nID, CWindow +// wndCtl) +#define CR_REFLECTED_COMMAND_RANGE_HANDLER_EX(idFirst, idLast, func) \ + if (uMsg == OCM_COMMAND && LOWORD(wParam) >= idFirst && \ + LOWORD(wParam) <= idLast) { \ + SetMsgHandled(TRUE); \ + func((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// void OnReflectedCommandRangeCodeHandlerEX(UINT uNotifyCode, int nID, CWindow +// wndCtl) +#define CR_REFLECTED_COMMAND_RANGE_CODE_HANDLER_EX( \ + idFirst, idLast, code, func) \ + if (uMsg == OCM_COMMAND && code == HIWORD(wParam) && \ + LOWORD(wParam) >= idFirst && LOWORD(wParam) <= idLast) { \ + SetMsgHandled(TRUE); \ + func((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \ + lResult = 0; \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// LRESULT OnReflectedNotifyRangeHandlerEX(LPNMHDR pnmh) +#define CR_REFLECTED_NOTIFY_RANGE_HANDLER_EX(idFirst, idLast, func) \ + if (uMsg == OCM_NOTIFY && ((LPNMHDR)lParam)->idFrom >= idFirst && \ + ((LPNMHDR)lParam)->idFrom <= idLast) { \ + SetMsgHandled(TRUE); \ + lResult = func((LPNMHDR)lParam); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +// LRESULT OnReflectedNotifyRangeCodeHandlerEX(LPNMHDR pnmh) +#define CR_REFLECTED_NOTIFY_RANGE_CODE_HANDLER_EX(idFirst, idLast, cd, func) \ + if (uMsg == OCM_NOTIFY && cd == ((LPNMHDR)lParam)->code && \ + ((LPNMHDR)lParam)->idFrom >= idFirst && \ + ((LPNMHDR)lParam)->idFrom <= idLast) { \ + SetMsgHandled(TRUE); \ + lResult = func((LPNMHDR)lParam); \ + if (IsMsgHandled()) \ + return TRUE; \ + } + +#define CR_DEFLATE_RECT(rect, by) \ + { \ + (rect)->left += (by)->left; \ + (rect)->top += (by)->top; \ + (rect)->right -= (by)->right; \ + (rect)->bottom -= (by)->bottom; \ + } + +#define CR_POINT_INITIALIZER_FROM_LPARAM(lparam) \ + { LOWORD(lparam), HIWORD(lparam) } + +#endif // UI_GFX_WIN_MSG_UTIL_H_ diff --git a/chromium/ui/gfx/win/singleton_hwnd.cc b/chromium/ui/gfx/win/singleton_hwnd.cc index 2de6a8b8a16..fc4d11f2d5e 100644 --- a/chromium/ui/gfx/win/singleton_hwnd.cc +++ b/chromium/ui/gfx/win/singleton_hwnd.cc @@ -37,8 +37,7 @@ BOOL SingletonHwnd::ProcessWindowMessage(HWND window, } SingletonHwnd::SingletonHwnd() { - if (!base::MessageLoop::current() || - base::MessageLoop::current()->type() != base::MessageLoop::TYPE_UI) { + if (!base::MessageLoopForUI::IsCurrent()) { // Creating this window in (e.g.) a renderer inhibits shutdown on // Windows. See http://crbug.com/230122 and http://crbug.com/236039. DLOG(ERROR) << "Cannot create windows on non-UI thread!"; diff --git a/chromium/ui/gfx/win/window_impl.cc b/chromium/ui/gfx/win/window_impl.cc index 7f0c232ae61..ff9fe57cf9d 100644 --- a/chromium/ui/gfx/win/window_impl.cc +++ b/chromium/ui/gfx/win/window_impl.cc @@ -97,7 +97,7 @@ ATOM ClassRegistrar::RetrieveClassAtom(const ClassInfo& class_info) { } // No class found, need to register one. - string16 name = string16(WindowImpl::kBaseClassName) + + base::string16 name = base::string16(WindowImpl::kBaseClassName) + base::IntToString16(registered_count_++); WNDCLASSEX window_class; @@ -226,10 +226,14 @@ HICON WindowImpl::GetDefaultWindowIcon() const { LRESULT WindowImpl::OnWndProc(UINT message, WPARAM w_param, LPARAM l_param) { LRESULT result = 0; + HWND hwnd = hwnd_; + if (message == WM_NCDESTROY) + hwnd_ = NULL; + // Handle the message if it's in our message map; otherwise, let the system // handle it. - if (!ProcessWindowMessage(hwnd_, message, w_param, l_param, result)) - result = DefWindowProc(hwnd_, message, w_param, l_param); + if (!ProcessWindowMessage(hwnd, message, w_param, l_param, result)) + result = DefWindowProc(hwnd, message, w_param, l_param); return result; } diff --git a/chromium/ui/gfx/win/window_impl.h b/chromium/ui/gfx/win/window_impl.h index 1825c0b2f00..8206562935f 100644 --- a/chromium/ui/gfx/win/window_impl.h +++ b/chromium/ui/gfx/win/window_impl.h @@ -5,17 +5,13 @@ #ifndef UI_GFX_WIN_WINDOW_IMPL_H_ #define UI_GFX_WIN_WINDOW_IMPL_H_ -#include <atlbase.h> -#include <atlapp.h> -#include <atlmisc.h> -#include <atlcrack.h> - #include <string> #include "base/logging.h" #include "ui/gfx/gfx_export.h" #include "ui/gfx/native_widget_types.h" #include "ui/gfx/rect.h" +#include "ui/gfx/win/msg_util.h" namespace gfx { diff --git a/chromium/ui/gfx/x/BUILD.gn b/chromium/ui/gfx/x/BUILD.gn new file mode 100644 index 00000000000..d7ae7ed4ab9 --- /dev/null +++ b/chromium/ui/gfx/x/BUILD.gn @@ -0,0 +1,32 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +component("x") { + output_name = "gfx_x11" + + sources = [ + "x11_atom_cache.cc", + "x11_atom_cache.h", + "x11_connection.cc", + "x11_connection.h", + "x11_error_tracker.cc", + "x11_error_tracker.h", + "x11_switches.cc", + "x11_switches.h", + "x11_types.cc", + "x11_types.h", + ] + + defines = [ + "GFX_IMPLEMENTATION", + ] + + configs += [ + "//build/config/linux:x11", + ] + + deps = [ + "//base", + ] +} diff --git a/chromium/ui/gfx/x/gfx_x11.gyp b/chromium/ui/gfx/x/gfx_x11.gyp new file mode 100644 index 00000000000..5be65ff1789 --- /dev/null +++ b/chromium/ui/gfx/x/gfx_x11.gyp @@ -0,0 +1,38 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'variables': { + 'chromium_code': 1, + 'use_x11': 1, # It is necessary to explicitly set use_x11 here to make sure + # that the exclusion rules in filename_rules.gypi do not + # exclude the x11* files. + }, + + 'targets': [ + { + 'target_name': 'gfx_x11', + 'type': '<(component)', + 'dependencies': [ + '../../../base/base.gyp:base', + '../../../build/linux/system.gyp:x11', + ], + 'defines': [ + 'GFX_IMPLEMENTATION', + ], + 'sources': [ + 'x11_atom_cache.cc', + 'x11_atom_cache.h', + 'x11_connection.cc', + 'x11_connection.h', + 'x11_error_tracker.cc', + 'x11_error_tracker.h', + 'x11_switches.cc', + 'x11_switches.h', + 'x11_types.cc', + 'x11_types.h', + ], + }, + ] +} diff --git a/chromium/ui/gfx/x/x11_atom_cache.cc b/chromium/ui/gfx/x/x11_atom_cache.cc index da75c0be34e..c9f52ca2aaa 100644 --- a/chromium/ui/gfx/x/x11_atom_cache.cc +++ b/chromium/ui/gfx/x/x11_atom_cache.cc @@ -5,20 +5,21 @@ #include "ui/gfx/x/x11_atom_cache.h" #include <X11/Xatom.h> +#include <X11/Xlib.h> #include "base/logging.h" #include "base/memory/scoped_ptr.h" namespace ui { -X11AtomCache::X11AtomCache(Display* xdisplay, const char** to_cache) +X11AtomCache::X11AtomCache(XDisplay* xdisplay, const char** to_cache) : xdisplay_(xdisplay), uncached_atoms_allowed_(false) { int cache_count = 0; for (const char** i = to_cache; *i != NULL; i++) cache_count++; - scoped_ptr< ::Atom[]> cached_atoms(new ::Atom[cache_count]); + scoped_ptr<Atom[]> cached_atoms(new Atom[cache_count]); // Grab all the atoms we need now to minimize roundtrips to the X11 server. XInternAtoms(xdisplay_, @@ -31,11 +32,11 @@ X11AtomCache::X11AtomCache(Display* xdisplay, const char** to_cache) X11AtomCache::~X11AtomCache() {} -::Atom X11AtomCache::GetAtom(const char* name) const { - std::map<std::string, ::Atom>::const_iterator it = cached_atoms_.find(name); +Atom X11AtomCache::GetAtom(const char* name) const { + std::map<std::string, Atom>::const_iterator it = cached_atoms_.find(name); if (uncached_atoms_allowed_ && it == cached_atoms_.end()) { - ::Atom atom = XInternAtom(xdisplay_, name, false); + Atom atom = XInternAtom(xdisplay_, name, false); cached_atoms_.insert(std::make_pair(name, atom)); return atom; } diff --git a/chromium/ui/gfx/x/x11_atom_cache.h b/chromium/ui/gfx/x/x11_atom_cache.h index fefa64205d0..6a29df720c6 100644 --- a/chromium/ui/gfx/x/x11_atom_cache.h +++ b/chromium/ui/gfx/x/x11_atom_cache.h @@ -5,16 +5,14 @@ #ifndef UI_GFX_X_X11_ATOM_CACHE_H_ #define UI_GFX_X_X11_ATOM_CACHE_H_ -#include "base/basictypes.h" -#include "ui/gfx/gfx_export.h" - #include <map> #include <string> -#include <X11/Xlib.h> +#include "base/basictypes.h" +#include "ui/gfx/gfx_export.h" -// Get rid of a macro from Xlib.h that conflicts with Aura's RootWindow class. -#undef RootWindow +typedef unsigned long Atom; +typedef struct _XDisplay XDisplay; namespace ui { @@ -25,22 +23,22 @@ namespace ui { class GFX_EXPORT X11AtomCache { public: // Preinterns the NULL terminated list of string |to_cache_ on |xdisplay|. - X11AtomCache(Display* xdisplay, const char** to_cache); + X11AtomCache(XDisplay* xdisplay, const char** to_cache); ~X11AtomCache(); // Returns the pre-interned Atom without having to go to the x server. - ::Atom GetAtom(const char*) const; + Atom GetAtom(const char*) const; // When an Atom isn't in the list of items we've cached, we should look it // up, cache it locally, and then return the result. void allow_uncached_atoms() { uncached_atoms_allowed_ = true; } private: - Display* xdisplay_; + XDisplay* xdisplay_; bool uncached_atoms_allowed_; - mutable std::map<std::string, ::Atom> cached_atoms_; + mutable std::map<std::string, Atom> cached_atoms_; DISALLOW_COPY_AND_ASSIGN(X11AtomCache); }; diff --git a/chromium/ui/gfx/x/x11_connection.cc b/chromium/ui/gfx/x/x11_connection.cc new file mode 100644 index 00000000000..c5ee77f40d8 --- /dev/null +++ b/chromium/ui/gfx/x/x11_connection.cc @@ -0,0 +1,17 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/gfx/x/x11_connection.h" + +#include <X11/Xlib.h> + +#include "ui/gfx/x/x11_types.h" + +namespace gfx { + +bool InitializeThreadedX11() { + return XInitThreads() && gfx::GetXDisplay(); +} + +} // namespace gfx diff --git a/chromium/ui/gfx/x/x11_connection.h b/chromium/ui/gfx/x/x11_connection.h new file mode 100644 index 00000000000..06ca89fe800 --- /dev/null +++ b/chromium/ui/gfx/x/x11_connection.h @@ -0,0 +1,18 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_GFX_X_X11_CONNECTION_H_ +#define UI_GFX_X_X11_CONNECTION_H_ + +#include "ui/gfx/gfx_export.h" + +namespace gfx { + +// Initializes thread support for X11, and opens a connection to the display. +// Return false if either fails, and true otherwise. +GFX_EXPORT bool InitializeThreadedX11(); + +} // namespace gfx + +#endif // UI_GFX_X_X11_CONNECTION_H_ diff --git a/chromium/ui/gfx/x/x11_error_tracker.cc b/chromium/ui/gfx/x/x11_error_tracker.cc new file mode 100644 index 00000000000..110f79ccd02 --- /dev/null +++ b/chromium/ui/gfx/x/x11_error_tracker.cc @@ -0,0 +1,45 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/logging.h" +#include "ui/gfx/x/x11_error_tracker.h" + +#include "ui/gfx/x/x11_types.h" + +namespace { + +unsigned char g_x11_error_code = 0; +static gfx::X11ErrorTracker* g_handler = NULL; + +int X11ErrorHandler(Display* display, XErrorEvent* error) { + g_x11_error_code = error->error_code; + return 0; +} +} + +namespace gfx { + +X11ErrorTracker::X11ErrorTracker() { + // This is a poor-man's check for incorrect usage. It disallows nested + // X11ErrorTracker instances on the same thread. + DCHECK(g_handler == NULL); + g_handler = this; + XSync(GetXDisplay(), False); + old_handler_ = XSetErrorHandler(X11ErrorHandler); + g_x11_error_code = 0; +} + +X11ErrorTracker::~X11ErrorTracker() { + g_handler = NULL; + XSetErrorHandler(old_handler_); +} + +bool X11ErrorTracker::FoundNewError() { + XSync(GetXDisplay(), False); + unsigned char error = g_x11_error_code; + g_x11_error_code = 0; + return error != 0; +} + +} // namespace gfx diff --git a/chromium/ui/gfx/x/x11_error_tracker.h b/chromium/ui/gfx/x/x11_error_tracker.h new file mode 100644 index 00000000000..efcac0c7099 --- /dev/null +++ b/chromium/ui/gfx/x/x11_error_tracker.h @@ -0,0 +1,36 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_GFX_X_X11_ERROR_TRACKER_H_ +#define UI_GFX_X_X11_ERROR_TRACKER_H_ + +#include <X11/Xlib.h> + +#include "base/basictypes.h" +#include "ui/gfx/gfx_export.h" + +namespace gfx { + +// X11ErrorTracker catches X11 errors in a non-fatal way. It does so by +// temporarily changing the X11 error handler. The old error handler is +// restored when the tracker is destroyed. +class GFX_EXPORT X11ErrorTracker { + public: + X11ErrorTracker(); + ~X11ErrorTracker(); + + // Returns whether an X11 error happened since this function was last called + // (or since the creation of the tracker). This is potentially expensive, + // since this causes a sync with the X server. + bool FoundNewError(); + + private: + XErrorHandler old_handler_; + + DISALLOW_COPY_AND_ASSIGN(X11ErrorTracker); +}; + +} // namespace gfx + +#endif // UI_GFX_X_X11_ERROR_TRACKER_H_ diff --git a/chromium/ui/gfx/x/x11_switches.cc b/chromium/ui/gfx/x/x11_switches.cc new file mode 100644 index 00000000000..bda0ad3c557 --- /dev/null +++ b/chromium/ui/gfx/x/x11_switches.cc @@ -0,0 +1,15 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/gfx/x/x11_switches.h" + +namespace switches { + +#if !defined(OS_CHROMEOS) +// Which X11 display to connect to. Emulates the GTK+ "--display=" command line +// argument. +const char kX11Display[] = "display"; +#endif + +} // namespace switches diff --git a/chromium/ui/gfx/x/x11_switches.h b/chromium/ui/gfx/x/x11_switches.h new file mode 100644 index 00000000000..4044c514154 --- /dev/null +++ b/chromium/ui/gfx/x/x11_switches.h @@ -0,0 +1,18 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_GFX_X_X11_SWITCHES_H_ +#define UI_GFX_X_X11_SWITCHES_H_ + +#include "ui/gfx/gfx_export.h" + +namespace switches { + +#if !defined(OS_CHROMEOS) +GFX_EXPORT extern const char kX11Display[]; +#endif + +} // namespace switches + +#endif // UI_GFX_X_X11_SWITCHES_H_ diff --git a/chromium/ui/gfx/x/x11_types.cc b/chromium/ui/gfx/x/x11_types.cc index 48ce6416b69..54a0816c652 100644 --- a/chromium/ui/gfx/x/x11_types.cc +++ b/chromium/ui/gfx/x/x11_types.cc @@ -6,12 +6,27 @@ #include <X11/Xlib.h> +#include "base/command_line.h" #include "base/message_loop/message_loop.h" +#include "ui/gfx/x/x11_switches.h" namespace gfx { XDisplay* GetXDisplay() { - return base::MessagePumpForUI::GetDefaultXDisplay(); + static XDisplay* display = NULL; + if (!display) + display = OpenNewXDisplay(); + return display; +} + +XDisplay* OpenNewXDisplay() { +#if defined(OS_CHROMEOS) + return XOpenDisplay(NULL); +#else + std::string display_str = base::CommandLine::ForCurrentProcess()-> + GetSwitchValueASCII(switches::kX11Display); + return XOpenDisplay(display_str.empty() ? NULL : display_str.c_str()); +#endif } void PutARGBImage(XDisplay* display, diff --git a/chromium/ui/gfx/x/x11_types.h b/chromium/ui/gfx/x/x11_types.h index 0f96fdc6a89..b9f1f9c1fce 100644 --- a/chromium/ui/gfx/x/x11_types.h +++ b/chromium/ui/gfx/x/x11_types.h @@ -20,6 +20,9 @@ namespace gfx { // chrome codebase to get the display from window. GFX_EXPORT XDisplay* GetXDisplay(); +// This opens a new X11 XDisplay*, taking command line arguments into account. +GFX_EXPORT XDisplay* OpenNewXDisplay(); + // Return the number of bits-per-pixel for a pixmap of the given depth GFX_EXPORT int BitsPerPixelForPixmapDepth(XDisplay* display, int depth); |