Skip to content

Commit

Permalink
Introduce user customization of external HighDPI mode for 4K monitor.
Browse files Browse the repository at this point in the history
This CL adds the options of customizing external display's device
scale factor from the options page. There are two TODOs though:
- the configured device scale factor should be stored
- not possible to specify device scale factor and resolution at the
  same time; from 1600x900, it can't enter to 1920x1080(2x). Instead
  it enters to 3840x2160, then the user needs to select 2x again.

Actually the former would solve the latter; this CL sets the new
resolution and the DSF at the same time but resolution change is
asynchronous, so changed DSF will be overwritten by the resolution
change.

I will address these issues in further CL(s).

BUG=396704
R=oshima@chromium.org, stevenjb@chromium.org
TBR=asvitkine@chromium.org
TEST=manually

Review URL: https://codereview.chromium.org/417113012

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@287677 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
mukai@chromium.org committed Aug 6, 2014
1 parent 9c1da58 commit 7badf7f
Show file tree
Hide file tree
Showing 16 changed files with 612 additions and 207 deletions.
70 changes: 61 additions & 9 deletions ash/display/display_change_observer_chromeos.cc
Original file line number Diff line number Diff line change
Expand Up @@ -46,25 +46,60 @@ const DeviceScaleFactorDPIThreshold kThresholdTable[] = {
// 1 inch in mm.
const float kInchInMm = 25.4f;

// Display mode list is sorted by (in descending priority):
// * the area in pixels.
// * refresh rate.
// The minimum pixel width whose monitor can be called as '4K'.
const int kMinimumWidthFor4K = 3840;

// The list of device scale factors (in addition to 1.0f) which is
// available in extrenal large monitors.
const float kAdditionalDeviceScaleFactorsFor4k[] = {1.25f, 2.0f};

// Display mode list is sorted by:
// * the area in pixels in ascending order
// * refresh rate in descending order
struct DisplayModeSorter {
bool operator()(const DisplayMode& a, const DisplayMode& b) {
if (a.size.GetArea() == b.size.GetArea())
gfx::Size size_a_dip = a.GetSizeInDIP();
gfx::Size size_b_dip = b.GetSizeInDIP();
if (size_a_dip.GetArea() == size_b_dip.GetArea())
return (a.refresh_rate > b.refresh_rate);
return (a.size.GetArea() > b.size.GetArea());
return (size_a_dip.GetArea() < size_b_dip.GetArea());
}
};

} // namespace

// static
std::vector<DisplayMode> DisplayChangeObserver::GetDisplayModeList(
std::vector<DisplayMode> DisplayChangeObserver::GetInternalDisplayModeList(
const DisplayInfo& display_info,
const DisplayConfigurator::DisplayState& output) {
std::vector<DisplayMode> display_mode_list;
const ui::DisplayMode* ui_native_mode = output.display->native_mode();
DisplayMode native_mode(ui_native_mode->size(),
ui_native_mode->refresh_rate(),
ui_native_mode->is_interlaced(),
true);
native_mode.device_scale_factor = display_info.device_scale_factor();
std::vector<float> ui_scales =
DisplayManager::GetScalesForDisplay(display_info);
for (size_t i = 0; i < ui_scales.size(); ++i) {
DisplayMode mode = native_mode;
mode.ui_scale = ui_scales[i];
mode.native = (ui_scales[i] == 1.0f);
display_mode_list.push_back(mode);
}

std::sort(
display_mode_list.begin(), display_mode_list.end(), DisplayModeSorter());
return display_mode_list;
}

// static
std::vector<DisplayMode> DisplayChangeObserver::GetExternalDisplayModeList(
const DisplayConfigurator::DisplayState& output) {
typedef std::map<std::pair<int, int>, DisplayMode> DisplayModeMap;
DisplayModeMap display_mode_map;

DisplayMode native_mode;
for (std::vector<const ui::DisplayMode*>::const_iterator it =
output.display->modes().begin();
it != output.display->modes().end();
Expand All @@ -76,6 +111,8 @@ std::vector<DisplayMode> DisplayChangeObserver::GetDisplayModeList(
mode_info.refresh_rate(),
mode_info.is_interlaced(),
output.display->native_mode() == *it);
if (display_mode.native)
native_mode = display_mode;

// Add the display mode if it isn't already present and override interlaced
// display modes with non-interlaced ones.
Expand All @@ -92,6 +129,17 @@ std::vector<DisplayMode> DisplayChangeObserver::GetDisplayModeList(
++iter) {
display_mode_list.push_back(iter->second);
}

if (native_mode.size.width() >= kMinimumWidthFor4K) {
for (size_t i = 0; i < arraysize(kAdditionalDeviceScaleFactorsFor4k);
++i) {
DisplayMode mode = native_mode;
mode.device_scale_factor = kAdditionalDeviceScaleFactorsFor4k[i];
mode.native = false;
display_mode_list.push_back(mode);
}
}

std::sort(
display_mode_list.begin(), display_mode_list.end(), DisplayModeSorter());
return display_mode_list;
Expand Down Expand Up @@ -150,8 +198,6 @@ void DisplayChangeObserver::OnDisplayModeChanged(
}
gfx::Rect display_bounds(state.display->origin(), mode_info->size());

std::vector<DisplayMode> display_modes = GetDisplayModeList(state);

std::string name =
state.display->type() == ui::DISPLAY_CONNECTION_TYPE_INTERNAL ?
l10n_util::GetStringUTF8(IDS_ASH_INTERNAL_DISPLAY_NAME) :
Expand All @@ -168,13 +214,19 @@ void DisplayChangeObserver::OnDisplayModeChanged(
new_info.set_device_scale_factor(device_scale_factor);
new_info.SetBounds(display_bounds);
new_info.set_native(true);
new_info.set_display_modes(display_modes);
new_info.set_touch_support(state.touch_device_id == 0 ?
gfx::Display::TOUCH_SUPPORT_UNAVAILABLE :
gfx::Display::TOUCH_SUPPORT_AVAILABLE);
new_info.set_touch_device_id(state.touch_device_id);
new_info.set_is_aspect_preserving_scaling(
state.display->is_aspect_preserving_scaling());

std::vector<DisplayMode> display_modes =
(state.display->type() == ui::DISPLAY_CONNECTION_TYPE_INTERNAL) ?
GetInternalDisplayModeList(new_info, state) :
GetExternalDisplayModeList(state);
new_info.set_display_modes(display_modes);

new_info.set_available_color_profiles(
Shell::GetInstance()
->display_configurator()
Expand Down
8 changes: 7 additions & 1 deletion ash/display/display_change_observer_chromeos.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

namespace ash {

class DisplayInfo;
struct DisplayMode;

// An object that observes changes in display configuration and
Expand All @@ -20,8 +21,13 @@ class DisplayChangeObserver : public ui::DisplayConfigurator::StateController,
public ui::DisplayConfigurator::Observer,
public ShellObserver {
public:
// Returns the mode list for internal display.
ASH_EXPORT static std::vector<DisplayMode> GetInternalDisplayModeList(
const DisplayInfo& display_info,
const ui::DisplayConfigurator::DisplayState& output);

// Returns the resolution list.
ASH_EXPORT static std::vector<DisplayMode> GetDisplayModeList(
ASH_EXPORT static std::vector<DisplayMode> GetExternalDisplayModeList(
const ui::DisplayConfigurator::DisplayState& output);

DisplayChangeObserver();
Expand Down
165 changes: 148 additions & 17 deletions ash/display/display_change_observer_chromeos_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ typedef testing::Test DisplayChangeObserverTest;

namespace ash {

TEST_F(DisplayChangeObserverTest, GetDisplayModeList) {
TEST_F(DisplayChangeObserverTest, GetExternalDisplayModeList) {
ScopedVector<const ui::DisplayMode> modes;
modes.push_back(new ui::DisplayMode(gfx::Size(1920, 1200), false, 60));

Expand Down Expand Up @@ -48,37 +48,168 @@ TEST_F(DisplayChangeObserverTest, GetDisplayModeList) {
output.display = &display_snapshot;

std::vector<DisplayMode> display_modes =
DisplayChangeObserver::GetDisplayModeList(output);
DisplayChangeObserver::GetExternalDisplayModeList(output);
ASSERT_EQ(6u, display_modes.size());
EXPECT_EQ("1920x1200", display_modes[0].size.ToString());
EXPECT_FALSE(display_modes[0].interlaced);
EXPECT_EQ("640x480", display_modes[0].size.ToString());
EXPECT_TRUE(display_modes[0].interlaced);
EXPECT_EQ(display_modes[0].refresh_rate, 60);

EXPECT_EQ("1920x1080", display_modes[1].size.ToString());
EXPECT_EQ("1024x600", display_modes[1].size.ToString());
EXPECT_FALSE(display_modes[1].interlaced);
EXPECT_EQ(display_modes[1].refresh_rate, 80);
EXPECT_EQ(display_modes[1].refresh_rate, 70);

EXPECT_EQ("1280x720", display_modes[2].size.ToString());
EXPECT_FALSE(display_modes[2].interlaced);
EXPECT_EQ(display_modes[2].refresh_rate, 60);
EXPECT_EQ("1024x768", display_modes[2].size.ToString());
EXPECT_TRUE(display_modes[2].interlaced);
EXPECT_EQ(display_modes[2].refresh_rate, 70);

EXPECT_EQ("1024x768", display_modes[3].size.ToString());
EXPECT_TRUE(display_modes[3].interlaced);
EXPECT_EQ(display_modes[3].refresh_rate, 70);
EXPECT_EQ("1280x720", display_modes[3].size.ToString());
EXPECT_FALSE(display_modes[3].interlaced);
EXPECT_EQ(display_modes[3].refresh_rate, 60);

EXPECT_EQ("1024x600", display_modes[4].size.ToString());
EXPECT_EQ("1920x1080", display_modes[4].size.ToString());
EXPECT_FALSE(display_modes[4].interlaced);
EXPECT_EQ(display_modes[4].refresh_rate, 70);
EXPECT_EQ(display_modes[4].refresh_rate, 80);

EXPECT_EQ("640x480", display_modes[5].size.ToString());
EXPECT_TRUE(display_modes[5].interlaced);
EXPECT_EQ("1920x1200", display_modes[5].size.ToString());
EXPECT_FALSE(display_modes[5].interlaced);
EXPECT_EQ(display_modes[5].refresh_rate, 60);

// Outputs without any modes shouldn't cause a crash.
modes.clear();
display_snapshot.set_modes(modes.get());

display_modes = DisplayChangeObserver::GetDisplayModeList(output);
display_modes = DisplayChangeObserver::GetExternalDisplayModeList(output);
EXPECT_EQ(0u, display_modes.size());
}

TEST_F(DisplayChangeObserverTest, GetInternalDisplayModeList) {
ScopedVector<const ui::DisplayMode> modes;
// Data picked from peppy.
modes.push_back(new ui::DisplayMode(gfx::Size(1366, 768), false, 60));
modes.push_back(new ui::DisplayMode(gfx::Size(1024, 768), false, 60));
modes.push_back(new ui::DisplayMode(gfx::Size(800, 600), false, 60));
modes.push_back(new ui::DisplayMode(gfx::Size(600, 600), false, 56.2));
modes.push_back(new ui::DisplayMode(gfx::Size(640, 480), false, 59.9));

ui::TestDisplaySnapshot display_snapshot;
display_snapshot.set_modes(modes.get());
display_snapshot.set_native_mode(modes[0]);
DisplayConfigurator::DisplayState output;
output.display = &display_snapshot;

DisplayInfo info;
info.SetBounds(gfx::Rect(0, 0, 1366, 768));

std::vector<DisplayMode> display_modes =
DisplayChangeObserver::GetInternalDisplayModeList(info, output);
ASSERT_EQ(5u, display_modes.size());
EXPECT_EQ("1366x768", display_modes[0].size.ToString());
EXPECT_FALSE(display_modes[0].native);
EXPECT_NEAR(display_modes[0].ui_scale, 0.5, 0.01);
EXPECT_EQ(display_modes[0].refresh_rate, 60);

EXPECT_EQ("1366x768", display_modes[1].size.ToString());
EXPECT_FALSE(display_modes[1].native);
EXPECT_NEAR(display_modes[1].ui_scale, 0.6, 0.01);
EXPECT_EQ(display_modes[1].refresh_rate, 60);

EXPECT_EQ("1366x768", display_modes[2].size.ToString());
EXPECT_FALSE(display_modes[2].native);
EXPECT_NEAR(display_modes[2].ui_scale, 0.75, 0.01);
EXPECT_EQ(display_modes[2].refresh_rate, 60);

EXPECT_EQ("1366x768", display_modes[3].size.ToString());
EXPECT_TRUE(display_modes[3].native);
EXPECT_NEAR(display_modes[3].ui_scale, 1.0, 0.01);
EXPECT_EQ(display_modes[3].refresh_rate, 60);

EXPECT_EQ("1366x768", display_modes[4].size.ToString());
EXPECT_FALSE(display_modes[4].native);
EXPECT_NEAR(display_modes[4].ui_scale, 1.125, 0.01);
EXPECT_EQ(display_modes[4].refresh_rate, 60);
}

TEST_F(DisplayChangeObserverTest, GetExternalDisplayModeList4K) {
ScopedVector<const ui::DisplayMode> modes;
modes.push_back(new ui::DisplayMode(gfx::Size(3840, 2160), false, 30));
modes.push_back(new ui::DisplayMode(gfx::Size(1920, 1200), false, 60));

// All non-interlaced (as would be seen with different refresh rates).
modes.push_back(new ui::DisplayMode(gfx::Size(1920, 1080), false, 80));
modes.push_back(new ui::DisplayMode(gfx::Size(1920, 1080), false, 70));
modes.push_back(new ui::DisplayMode(gfx::Size(1920, 1080), false, 60));

// Interlaced vs non-interlaced.
modes.push_back(new ui::DisplayMode(gfx::Size(1280, 720), true, 60));
modes.push_back(new ui::DisplayMode(gfx::Size(1280, 720), false, 60));

// Interlaced only.
modes.push_back(new ui::DisplayMode(gfx::Size(1024, 768), true, 70));
modes.push_back(new ui::DisplayMode(gfx::Size(1024, 768), true, 60));

// Mixed.
modes.push_back(new ui::DisplayMode(gfx::Size(1024, 600), true, 60));
modes.push_back(new ui::DisplayMode(gfx::Size(1024, 600), false, 70));
modes.push_back(new ui::DisplayMode(gfx::Size(1024, 600), false, 60));

// Just one interlaced mode.
modes.push_back(new ui::DisplayMode(gfx::Size(640, 480), true, 60));

ui::TestDisplaySnapshot display_snapshot;
display_snapshot.set_modes(modes.get());
display_snapshot.set_native_mode(modes[0]);
DisplayConfigurator::DisplayState output;
output.display = &display_snapshot;

std::vector<DisplayMode> display_modes =
DisplayChangeObserver::GetExternalDisplayModeList(output);
ASSERT_EQ(9u, display_modes.size());
EXPECT_EQ("640x480", display_modes[0].size.ToString());
EXPECT_TRUE(display_modes[0].interlaced);
EXPECT_EQ(display_modes[0].refresh_rate, 60);

EXPECT_EQ("1024x600", display_modes[1].size.ToString());
EXPECT_FALSE(display_modes[1].interlaced);
EXPECT_EQ(display_modes[1].refresh_rate, 70);

EXPECT_EQ("1024x768", display_modes[2].size.ToString());
EXPECT_TRUE(display_modes[2].interlaced);
EXPECT_EQ(display_modes[2].refresh_rate, 70);

EXPECT_EQ("1280x720", display_modes[3].size.ToString());
EXPECT_FALSE(display_modes[3].interlaced);
EXPECT_EQ(display_modes[3].refresh_rate, 60);

EXPECT_EQ("1920x1080", display_modes[4].size.ToString());
EXPECT_FALSE(display_modes[4].interlaced);
EXPECT_EQ(display_modes[4].refresh_rate, 80);

EXPECT_EQ("3840x2160", display_modes[5].size.ToString());
EXPECT_FALSE(display_modes[5].interlaced);
EXPECT_FALSE(display_modes[5].native);
EXPECT_EQ(display_modes[5].refresh_rate, 30);
EXPECT_EQ(display_modes[5].device_scale_factor, 2.0);

EXPECT_EQ("1920x1200", display_modes[6].size.ToString());
EXPECT_FALSE(display_modes[6].interlaced);
EXPECT_EQ(display_modes[6].refresh_rate, 60);

EXPECT_EQ("3840x2160", display_modes[7].size.ToString());
EXPECT_FALSE(display_modes[7].interlaced);
EXPECT_FALSE(display_modes[7].native);
EXPECT_EQ(display_modes[7].refresh_rate, 30);
EXPECT_EQ(display_modes[7].device_scale_factor, 1.25);

EXPECT_EQ("3840x2160", display_modes[8].size.ToString());
EXPECT_FALSE(display_modes[8].interlaced);
EXPECT_TRUE(display_modes[8].native);
EXPECT_EQ(display_modes[8].refresh_rate, 30);

// Outputs without any modes shouldn't cause a crash.
modes.clear();
display_snapshot.set_modes(modes.get());

display_modes = DisplayChangeObserver::GetExternalDisplayModeList(output);
EXPECT_EQ(0u, display_modes.size());
}

Expand Down
24 changes: 22 additions & 2 deletions ash/display/display_info.cc
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@ bool allow_upgrade_to_high_dpi = false;
}

DisplayMode::DisplayMode()
: refresh_rate(0.0f), interlaced(false), native(false) {}
: refresh_rate(0.0f),
interlaced(false),
native(false),
ui_scale(1.0f),
device_scale_factor(1.0f) {}

DisplayMode::DisplayMode(const gfx::Size& size,
float refresh_rate,
Expand All @@ -38,7 +42,23 @@ DisplayMode::DisplayMode(const gfx::Size& size,
: size(size),
refresh_rate(refresh_rate),
interlaced(interlaced),
native(native) {}
native(native),
ui_scale(1.0f),
device_scale_factor(1.0f) {}

gfx::Size DisplayMode::GetSizeInDIP() const {
gfx::SizeF size_dip(size);
size_dip.Scale(ui_scale);
size_dip.Scale(1.0f / device_scale_factor);
return gfx::ToFlooredSize(size_dip);
}

bool DisplayMode::IsEquivalent(const DisplayMode& other) const {
const float kEpsilon = 0.0001f;
return size == other.size &&
std::abs(ui_scale - other.ui_scale) < kEpsilon &&
std::abs(device_scale_factor - other.device_scale_factor) < kEpsilon;
}

// satic
DisplayInfo DisplayInfo::CreateFromSpec(const std::string& spec) {
Expand Down
8 changes: 8 additions & 0 deletions ash/display/display_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,18 @@ struct ASH_EXPORT DisplayMode {
bool interlaced,
bool native);

// Returns the size in DIP which isvisible to the user.
gfx::Size GetSizeInDIP() const;

// Returns true if |other| has same size and scale factors.
bool IsEquivalent(const DisplayMode& other) const;

gfx::Size size; // Physical pixel size of the display.
float refresh_rate; // Refresh rate of the display, in Hz.
bool interlaced; // True if mode is interlaced.
bool native; // True if mode is native mode of the display.
float ui_scale; // The UI scale factor of the mode.
float device_scale_factor; // The device scale factor of the mode.
};

// DisplayInfo contains metadata for each display. This is used to
Expand Down
Loading

0 comments on commit 7badf7f

Please sign in to comment.