summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorxiyuan@chromium.org <xiyuan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-04-11 02:23:56 +0000
committerxiyuan@chromium.org <xiyuan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-04-11 02:23:56 +0000
commit0834e1b153c28dec0ae9e90080ad7a106f1c8c84 (patch)
tree3a939399a68c91722271573bf2589266d642d58f
parentc8678d8870826350577c3f00e922e442abb647c9 (diff)
downloadchromium_src-0834e1b153c28dec0ae9e90080ad7a106f1c8c84.zip
chromium_src-0834e1b153c28dec0ae9e90080ad7a106f1c8c84.tar.gz
chromium_src-0834e1b153c28dec0ae9e90080ad7a106f1c8c84.tar.bz2
ash: Better and faster text shadows.
- Add a DrawStringWithShadows that draws text with shadows to canvas skia and RenderText; - Use DrawStringWithShadows in DropShadowLabel for better and faster text shadows; BUG=121694 TEST=Verify fix for issue 121694. Review URL: http://codereview.chromium.org/10008027 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@131696 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--ash/app_list/app_list_item_view.cc10
-rw-r--r--ash/app_list/drop_shadow_label.cc107
-rw-r--r--ash/app_list/drop_shadow_label.h28
-rw-r--r--ui/gfx/canvas.cc19
-rw-r--r--ui/gfx/canvas.h15
-rw-r--r--ui/gfx/canvas_android.cc11
-rw-r--r--ui/gfx/canvas_linux.cc18
-rw-r--r--ui/gfx/canvas_mac.mm18
-rw-r--r--ui/gfx/canvas_skia.cc45
-rw-r--r--ui/gfx/canvas_win.cc23
-rw-r--r--ui/gfx/render_text.cc58
-rw-r--r--ui/gfx/render_text.h13
-rw-r--r--ui/gfx/render_text_linux.cc1
-rw-r--r--ui/gfx/render_text_win.cc1
-rw-r--r--ui/gfx/shadow_value.cc61
-rw-r--r--ui/gfx/shadow_value.h58
-rw-r--r--ui/gfx/shadow_value_unittest.cc65
-rw-r--r--ui/ui.gyp2
-rw-r--r--ui/ui_unittests.gypi1
19 files changed, 408 insertions, 146 deletions
diff --git a/ash/app_list/app_list_item_view.cc b/ash/app_list/app_list_item_view.cc
index 5addda1..c9e7492 100644
--- a/ash/app_list/app_list_item_view.cc
+++ b/ash/app_list/app_list_item_view.cc
@@ -17,6 +17,7 @@
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/font.h"
+#include "ui/gfx/shadow_value.h"
#include "ui/gfx/skbitmap_operations.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/controls/menu/menu_item_view.h"
@@ -184,7 +185,14 @@ AppListItemView::AppListItemView(AppListModelView* list_model_view,
ALLOW_THIS_IN_INITIALIZER_LIST(apply_shadow_factory_(this)) {
title_->SetBackgroundColor(0);
title_->SetEnabledColor(kTitleColor);
- title_->SetDropShadowSize(3);
+
+ const gfx::ShadowValue kTitleShadows[] = {
+ gfx::ShadowValue(gfx::Point(0, 0), 1, SkColorSetARGB(0x66, 0, 0, 0)),
+ gfx::ShadowValue(gfx::Point(0, 0), 10, SkColorSetARGB(0x66, 0, 0, 0)),
+ gfx::ShadowValue(gfx::Point(0, 2), 2, SkColorSetARGB(0x66, 0, 0, 0)),
+ gfx::ShadowValue(gfx::Point(0, 2), 4, SkColorSetARGB(0x66, 0, 0, 0)),
+ };
+ title_->SetTextShadows(arraysize(kTitleShadows), kTitleShadows);
AddChildView(icon_);
AddChildView(title_);
diff --git a/ash/app_list/drop_shadow_label.cc b/ash/app_list/drop_shadow_label.cc
index de1c90f..c897e2a 100644
--- a/ash/app_list/drop_shadow_label.cc
+++ b/ash/app_list/drop_shadow_label.cc
@@ -8,97 +8,49 @@
#include "third_party/skia/include/effects/SkGradientShader.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/color_utils.h"
+#include "ui/gfx/insets.h"
#include "ui/gfx/skbitmap_operations.h"
using views::Label;
namespace ash {
-static const int kDefaultDropShadowSize = 2;
+DropShadowLabel::DropShadowLabel() {
+}
-DropShadowLabel::DropShadowLabel() : drop_shadow_size_(kDefaultDropShadowSize) {
+DropShadowLabel::~DropShadowLabel() {
}
-void DropShadowLabel::SetDropShadowSize(int drop_shadow_size) {
- if (drop_shadow_size != drop_shadow_size_) {
- drop_shadow_size_ = drop_shadow_size;
- invalidate_text_size();
- SchedulePaint();
+void DropShadowLabel::SetTextShadows(int shadow_count,
+ const gfx::ShadowValue* shadows) {
+ text_shadows_.clear();
+
+ if (shadow_count && shadows) {
+ for (int i = 0; i < shadow_count; ++i)
+ text_shadows_.push_back(shadows[i]);
}
}
+gfx::Insets DropShadowLabel::GetInsets() const {
+ gfx::Insets insets = views::Label::GetInsets();
+ gfx::Insets shadow_margin = gfx::ShadowValue::GetMargin(text_shadows_);
+ // Negate |shadow_margin| to convert it to a padding insets needed inside
+ // the bounds and combine with label's insets.
+ insets += -shadow_margin;
+ return insets;
+}
+
void DropShadowLabel::PaintText(gfx::Canvas* canvas,
const string16& text,
const gfx::Rect& text_bounds,
int flags) {
SkColor text_color = enabled() ? enabled_color() : disabled_color();
- if (drop_shadow_size_ > 0) {
- // To properly render shadow with elliding fade effect, text and shadow
- // is rendered to this canvas first with elliding disable so that underlying
- // code would not mix shadow color into text area because of elliding fade.
- // When that is done and if we need elliding fade, an alpha mask is applied
- // when transfering contents on this canvas to target canvas.
- gfx::Size canvas_size(text_bounds.width() + drop_shadow_size_,
- text_bounds.height() + drop_shadow_size_);
- gfx::Canvas text_canvas(canvas_size, false);
-
- const double kShadowOpacity = 0.2;
- const SkColor shadow_color =
- SkColorSetA(SK_ColorBLACK, kShadowOpacity * SkColorGetA(text_color));
- gfx::Size text_size = GetTextSize();
- for (int i = 0; i < drop_shadow_size_; i++) {
- text_canvas.DrawStringInt(text, font(), shadow_color, i, 0,
- text_size.width(), text_size.height(),
- flags | gfx::Canvas::NO_ELLIPSIS);
- text_canvas.DrawStringInt(text, font(), shadow_color, i, i,
- text_size.width(), text_size.height(),
- flags | gfx::Canvas::NO_ELLIPSIS);
- text_canvas.DrawStringInt(text, font(), shadow_color, 0, i,
- text_size.width(), text_size.height(),
- flags | gfx::Canvas::NO_ELLIPSIS);
- }
- text_canvas.DrawStringInt(text, font(), text_color, 0, 0,
- text_size.width(), text_size.height(),
- flags | gfx::Canvas::NO_ELLIPSIS);
-
- const SkBitmap& text_bitmap = const_cast<SkBitmap&>(
- skia::GetTopDevice(*text_canvas.sk_canvas())->accessBitmap(false));
-
- if (text_size.width() > text_bounds.width() &&
- !(flags & gfx::Canvas::NO_ELLIPSIS)) {
- // Apply an gradient alpha mask for elliding fade effect.
- const double kFadeWidthFactor = 1.5;
- int fade_width = std::min(text_size.width() / 2,
- static_cast<int>(text_size.height() * kFadeWidthFactor));
-
- const SkColor kColors[] = { SK_ColorWHITE, 0 };
- const SkScalar kPoints[] = { SkIntToScalar(0), SkIntToScalar(1) };
- SkPoint p[2];
- p[0].set(SkIntToScalar(text_bounds.width() - fade_width),
- SkIntToScalar(0));
- p[1].set(SkIntToScalar(text_bounds.width()),
- SkIntToScalar(0));
- SkShader* s = SkGradientShader::CreateLinear(
- p, kColors, kPoints, 2, SkShader::kClamp_TileMode, NULL);
-
- SkPaint paint;
- paint.setShader(s)->unref();
-
- gfx::Canvas alpha_canvas(canvas_size, false);
- alpha_canvas.DrawRect(gfx::Rect(canvas_size), paint);
-
- const SkBitmap& alpha_bitmap = const_cast<SkBitmap&>(
- skia::GetTopDevice(*alpha_canvas.sk_canvas())->accessBitmap(false));
- SkBitmap blended = SkBitmapOperations::CreateMaskedBitmap(text_bitmap,
- alpha_bitmap);
- canvas->DrawBitmapInt(blended, text_bounds.x(), text_bounds.y());
- } else {
- canvas->DrawBitmapInt(text_bitmap, text_bounds.x(), text_bounds.y());
- }
- } else {
- canvas->DrawStringInt(text, font(), text_color, text_bounds.x(),
- text_bounds.y(), text_bounds.width(), text_bounds.height(), flags);
- }
+ canvas->DrawStringWithShadows(text,
+ font(),
+ text_color,
+ text_bounds,
+ flags,
+ text_shadows_);
if (HasFocus() || paint_as_focused()) {
gfx::Rect focus_bounds = text_bounds;
@@ -108,11 +60,4 @@ void DropShadowLabel::PaintText(gfx::Canvas* canvas,
}
}
-gfx::Size DropShadowLabel::GetTextSize() const {
- gfx::Size text_size = Label::GetTextSize();
- text_size.SetSize(text_size.width() + drop_shadow_size_,
- text_size.height() + drop_shadow_size_);
- return text_size;
-}
-
} // namespace ash
diff --git a/ash/app_list/drop_shadow_label.h b/ash/app_list/drop_shadow_label.h
index 2a7ce5d..25a24e1 100644
--- a/ash/app_list/drop_shadow_label.h
+++ b/ash/app_list/drop_shadow_label.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// 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.
@@ -6,7 +6,9 @@
#define ASH_APP_LIST_DROP_SHADOW_LABEL_H_
#pragma once
-#include "ui/gfx/font.h"
+#include <vector>
+
+#include "ui/gfx/shadow_value.h"
#include "ui/views/controls/label.h"
namespace ash {
@@ -22,29 +24,19 @@ namespace ash {
class DropShadowLabel : public views::Label {
public:
DropShadowLabel();
+ virtual ~DropShadowLabel();
- // Sets the size of the drop shadow drawn under the text.
- // Defaults to two. Note that this is a really simplistic drop
- // shadow -- it gets more expensive to draw the larger it gets,
- // since it simply draws more copies of the string. For instance,
- // for a value of two, the string is draw seven times. In general,
- // it is drawn three extra times for each increment of |size|.
- void SetDropShadowSize(int size);
-
- // Return the size of the drop shadow in pixels.
- int drop_shadow_size() const { return drop_shadow_size_; }
+ void SetTextShadows(int shadow_count, const gfx::ShadowValue* shadows);
- // Overridden to paint the text differently from the base class.
+ private:
+ // Overridden from views::Label:
+ virtual gfx::Insets GetInsets() const OVERRIDE;
virtual void PaintText(gfx::Canvas* canvas,
const string16& text,
const gfx::Rect& text_bounds,
int flags) OVERRIDE;
- protected:
- virtual gfx::Size GetTextSize() const OVERRIDE;
-
- private:
- int drop_shadow_size_;
+ std::vector<gfx::ShadowValue> text_shadows_;
DISALLOW_COPY_AND_ASSIGN(DropShadowLabel);
};
diff --git a/ui/gfx/canvas.cc b/ui/gfx/canvas.cc
index ded3e2f7..883a7ce 100644
--- a/ui/gfx/canvas.cc
+++ b/ui/gfx/canvas.cc
@@ -13,6 +13,7 @@
#include "ui/gfx/canvas.h"
#include "ui/gfx/font.h"
#include "ui/gfx/rect.h"
+#include "ui/gfx/shadow_value.h"
#include "ui/gfx/skia_util.h"
#include "ui/gfx/transform.h"
@@ -340,6 +341,19 @@ void Canvas::DrawStringInt(const string16& text,
display_rect.width(), display_rect.height());
}
+void Canvas::DrawStringInt(const string16& text,
+ const gfx::Font& font,
+ SkColor color,
+ int x, int y, int w, int h,
+ int flags) {
+ DrawStringWithShadows(text,
+ font,
+ color,
+ gfx::Rect(x, y, w, h),
+ flags,
+ std::vector<ShadowValue>());
+}
+
void Canvas::TileImageInt(const SkBitmap& bitmap,
int x, int y, int w, int h) {
TileImageInt(bitmap, 0, 0, x, y, w, h);
@@ -389,4 +403,9 @@ bool Canvas::IntersectsClipRectInt(int x, int y, int w, int h) {
SkIntToScalar(y + h));
}
+bool Canvas::IntersectsClipRect(const gfx::Rect& rect) {
+ return IntersectsClipRectInt(rect.x(), rect.y(),
+ rect.width(), rect.height());
+}
+
} // namespace gfx
diff --git a/ui/gfx/canvas.h b/ui/gfx/canvas.h
index 7bb83b7..dd2e764 100644
--- a/ui/gfx/canvas.h
+++ b/ui/gfx/canvas.h
@@ -6,6 +6,8 @@
#define UI_GFX_CANVAS_H_
#pragma once
+#include <vector>
+
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "base/string16.h"
@@ -24,6 +26,7 @@ class Brush;
class Rect;
class Font;
class Point;
+class ShadowValue;
class Size;
// Canvas is a SkCanvas wrapper that provides a number of methods for
@@ -291,6 +294,15 @@ class UI_EXPORT Canvas {
int x, int y, int w, int h,
int flags);
+ // Similar to above DrawStringInt method but with text shadows support.
+ // Currently it's only implemented for canvas skia.
+ void DrawStringWithShadows(const string16& text,
+ const gfx::Font& font,
+ SkColor color,
+ const gfx::Rect& text_bounds,
+ int flags,
+ const std::vector<ShadowValue>& shadows);
+
// Draws a dotted gray rectangle used for focus purposes.
void DrawFocusRect(const gfx::Rect& rect);
@@ -332,6 +344,7 @@ class UI_EXPORT Canvas {
private:
// Test whether the provided rectangle intersects the current clip rect.
bool IntersectsClipRectInt(int x, int y, int w, int h);
+ bool IntersectsClipRect(const gfx::Rect& rect);
#if defined(OS_WIN)
// Draws text with the specified color, font and location. The text is
@@ -340,7 +353,7 @@ class UI_EXPORT Canvas {
void DrawStringInt(const string16& text,
HFONT font,
SkColor color,
- int x, int y, int w, int h,
+ const gfx::Rect& text_bounds,
int flags);
#endif
diff --git a/ui/gfx/canvas_android.cc b/ui/gfx/canvas_android.cc
index 8d24d3a..3d8a823 100644
--- a/ui/gfx/canvas_android.cc
+++ b/ui/gfx/canvas_android.cc
@@ -16,11 +16,12 @@ void Canvas::SizeStringInt(const string16& text,
NOTIMPLEMENTED();
}
-void Canvas::DrawStringInt(const string16& text,
- const gfx::Font& font,
- SkColor color,
- int x, int y, int w, int h,
- int flags) {
+void Canvas::DrawStringWithShadows(const string16& text,
+ const gfx::Font& font,
+ SkColor color,
+ const gfx::Rect& text_bounds,
+ int flags,
+ const std::vector<ShadowValue>& shadows) {
NOTIMPLEMENTED();
}
diff --git a/ui/gfx/canvas_linux.cc b/ui/gfx/canvas_linux.cc
index a47cb51..18ce2a4 100644
--- a/ui/gfx/canvas_linux.cc
+++ b/ui/gfx/canvas_linux.cc
@@ -244,16 +244,18 @@ void Canvas::DrawStringWithHalo(const string16& text,
context.DrawWithHalo(text_color, halo_color);
}
-void Canvas::DrawStringInt(const string16& text,
- const gfx::Font& font,
- SkColor color,
- int x, int y, int w, int h,
- int flags) {
- if (!IntersectsClipRectInt(x, y, w, h))
+void Canvas::DrawStringWithShadows(const string16& text,
+ const gfx::Font& font,
+ SkColor color,
+ const gfx::Rect& text_bounds,
+ int flags,
+ const std::vector<ShadowValue>& shadows) {
+ DLOG_IF(WARNING, !shadows.empty()) << "Text shadow not implemented.";
+
+ if (!IntersectsClipRect(text_bounds))
return;
- gfx::Rect bounds(x, y, w, h);
- DrawStringContext context(this, text, font, bounds, bounds, flags);
+ DrawStringContext context(this, text, font, text_bounds, text_bounds, flags);
context.Draw(color);
}
diff --git a/ui/gfx/canvas_mac.mm b/ui/gfx/canvas_mac.mm
index 76e61e2..23025e9 100644
--- a/ui/gfx/canvas_mac.mm
+++ b/ui/gfx/canvas_mac.mm
@@ -10,6 +10,7 @@
#include "base/sys_string_conversions.h"
#include "third_party/skia/include/core/SkTypeface.h"
#include "ui/gfx/font.h"
+#include "ui/gfx/rect.h"
// Note: This is a temporary Skia-based implementation of the ui/gfx text
// rendering routines for views/aura. It replaces the stale Cocoa-based
@@ -50,11 +51,14 @@ void Canvas::SizeStringInt(const string16& text,
*height = font.GetHeight();
}
-void Canvas::DrawStringInt(const string16& text,
- const gfx::Font& font,
- SkColor color,
- int x, int y, int w, int h,
- int flags) {
+void Canvas::DrawStringWithShadows(const string16& text,
+ const gfx::Font& font,
+ SkColor color,
+ const gfx::Rect& text_bounds,
+ int flags,
+ const std::vector<ShadowValue>& shadows) {
+ DLOG_IF(WARNING, !shadows.empty()) << "Text shadow not implemented.";
+
SkTypeface* typeface = SkTypeface::CreateFromName(font.GetFontName().c_str(),
FontTypefaceStyle(font));
SkPaint paint;
@@ -63,8 +67,8 @@ void Canvas::DrawStringInt(const string16& text,
paint.setColor(color);
canvas_->drawText(text.c_str(),
text.size() * sizeof(string16::value_type),
- x,
- y + h,
+ text_bounds.x(),
+ text_bounds.bottom(),
paint);
}
diff --git a/ui/gfx/canvas_skia.cc b/ui/gfx/canvas_skia.cc
index b790076..cdd4ec5 100644
--- a/ui/gfx/canvas_skia.cc
+++ b/ui/gfx/canvas_skia.cc
@@ -11,8 +11,10 @@
#include "ui/base/text/text_elider.h"
#include "ui/gfx/font.h"
#include "ui/gfx/font_list.h"
+#include "ui/gfx/insets.h"
#include "ui/gfx/rect.h"
#include "ui/gfx/render_text.h"
+#include "ui/gfx/shadow_value.h"
#include "ui/gfx/skia_util.h"
namespace {
@@ -218,12 +220,13 @@ void Canvas::SizeStringInt(const string16& text,
}
}
-void Canvas::DrawStringInt(const string16& text,
- const gfx::Font& font,
- SkColor color,
- int x, int y, int w, int h,
- int flags) {
- if (!IntersectsClipRectInt(x, y, w, h))
+void Canvas::DrawStringWithShadows(const string16& text,
+ const gfx::Font& font,
+ SkColor color,
+ const gfx::Rect& text_bounds,
+ int flags,
+ const std::vector<ShadowValue>& shadows) {
+ if (!IntersectsClipRect(text_bounds))
return;
flags = AdjustPlatformSpecificFlags(text, flags);
@@ -237,10 +240,13 @@ void Canvas::DrawStringInt(const string16& text,
}
#endif
- gfx::Rect rect(x, y, w, h);
+ gfx::Rect clip_rect(text_bounds);
+ clip_rect.Inset(ShadowValue::GetMargin(shadows));
+
canvas_->save(SkCanvas::kClip_SaveFlag);
- ClipRect(rect);
+ ClipRect(clip_rect);
+ gfx::Rect rect(text_bounds);
string16 adjusted_text = text;
#if defined(OS_WIN)
@@ -249,6 +255,7 @@ void Canvas::DrawStringInt(const string16& text,
#endif
scoped_ptr<RenderText> render_text(RenderText::CreateRenderText());
+ render_text->SetTextShadows(shadows);
if (flags & MULTI_LINE) {
ui::WordWrapBehavior wrap_behavior = ui::IGNORE_LONG_WORDS;
@@ -258,7 +265,10 @@ void Canvas::DrawStringInt(const string16& text,
wrap_behavior = ui::ELIDE_LONG_WORDS;
std::vector<string16> strings;
- ui::ElideRectangleText(adjusted_text, font, w, h, wrap_behavior,
+ ui::ElideRectangleText(adjusted_text,
+ font,
+ text_bounds.width(), text_bounds.height(),
+ wrap_behavior,
&strings);
for (size_t i = 0; i < strings.size(); i++) {
@@ -269,8 +279,11 @@ void Canvas::DrawStringInt(const string16& text,
// first line. This may not be correct if different lines in the text have
// different heights, but avoids needing to do two passes.
const int line_height = render_text->GetStringSize().height();
- if (i == 0)
- rect.Offset(0, VAlignText(strings.size() * line_height, flags, h));
+ if (i == 0) {
+ rect.Offset(0, VAlignText(strings.size() * line_height,
+ flags,
+ text_bounds.height()));
+ }
rect.set_height(line_height);
ApplyUnderlineStyle(range, render_text.get());
@@ -293,14 +306,18 @@ void Canvas::DrawStringInt(const string16& text,
}
#endif
- if (elide_text)
- ElideTextAndAdjustRange(font, w, &adjusted_text, &range);
+ if (elide_text) {
+ ElideTextAndAdjustRange(font,
+ text_bounds.width(),
+ &adjusted_text,
+ &range);
+ }
UpdateRenderText(rect, adjusted_text, font, flags, color,
render_text.get());
const int line_height = render_text->GetStringSize().height();
- rect.Offset(0, VAlignText(line_height, flags, h));
+ rect.Offset(0, VAlignText(line_height, flags, text_bounds.height()));
rect.set_height(line_height);
render_text->SetDisplayRect(rect);
diff --git a/ui/gfx/canvas_win.cc b/ui/gfx/canvas_win.cc
index 54ba117..0d3ca63 100644
--- a/ui/gfx/canvas_win.cc
+++ b/ui/gfx/canvas_win.cc
@@ -325,15 +325,15 @@ void Canvas::SizeStringInt(const string16& text,
void Canvas::DrawStringInt(const string16& text,
HFONT font,
SkColor color,
- int x, int y, int w, int h,
+ const gfx::Rect& text_bounds,
int flags) {
SkRect fclip;
if (!canvas_->getClipBounds(&fclip))
return;
- RECT text_bounds = { x, y, x + w, y + h };
+ RECT text_rect = text_bounds.ToRECT();
SkIRect clip;
fclip.round(&clip);
- if (!clip.intersect(skia::RECTToSkIRect(text_bounds)))
+ if (!clip.intersect(skia::RECTToSkIRect(text_rect)))
return;
// Clamp the max amount of text we'll draw to 32K. There seem to be bugs in
@@ -355,7 +355,7 @@ void Canvas::DrawStringInt(const string16& text,
SetTextColor(dc, brush_color);
int f = ComputeFormatFlags(flags, clamped_string);
- DoDrawText(dc, clamped_string, &text_bounds, f);
+ DoDrawText(dc, clamped_string, &text_rect, f);
}
// Restore the old font. This way we don't have to worry if the caller
@@ -369,12 +369,15 @@ void Canvas::DrawStringInt(const string16& text,
clip.height());
}
-void Canvas::DrawStringInt(const string16& text,
- const gfx::Font& font,
- SkColor color,
- int x, int y, int w, int h,
- int flags) {
- DrawStringInt(text, font.GetNativeFont(), color, x, y, w, h, flags);
+void Canvas::DrawStringWithShadows(const string16& text,
+ const gfx::Font& font,
+ SkColor color,
+ const gfx::Rect& text_bounds,
+ int flags,
+ const std::vector<ShadowValue>& shadows) {
+ DLOG_IF(WARNING, !shadows.empty()) << "Text shadow not implemented.";
+
+ DrawStringInt(text, font.GetNativeFont(), color, text_bounds, flags);
}
// Checks each pixel immediately adjacent to the given pixel in the bitmap. If
diff --git a/ui/gfx/render_text.cc b/ui/gfx/render_text.cc
index d7fa03c..6fff6f7 100644
--- a/ui/gfx/render_text.cc
+++ b/ui/gfx/render_text.cc
@@ -10,11 +10,16 @@
#include "base/i18n/break_iterator.h"
#include "base/logging.h"
#include "base/stl_util.h"
+#include "third_party/skia/include/core/SkColorFilter.h"
#include "third_party/skia/include/core/SkTypeface.h"
+#include "third_party/skia/include/effects/SkBlurMaskFilter.h"
#include "third_party/skia/include/effects/SkGradientShader.h"
+#include "third_party/skia/include/effects/SkLayerDrawLooper.h"
#include "ui/base/text/utf16_indexing.h"
#include "ui/gfx/canvas.h"
+#include "ui/gfx/insets.h"
#include "ui/gfx/native_theme.h"
+#include "ui/gfx/shadow_value.h"
namespace {
@@ -183,6 +188,10 @@ SkiaTextRenderer::SkiaTextRenderer(Canvas* canvas)
SkiaTextRenderer::~SkiaTextRenderer() {
}
+void SkiaTextRenderer::SetDrawLooper(SkDrawLooper* draw_looper) {
+ paint_.setLooper(draw_looper);
+}
+
void SkiaTextRenderer::SetFontSmoothingSettings(bool enable_smoothing,
bool enable_lcd_text) {
paint_.setAntiAlias(enable_smoothing);
@@ -546,8 +555,11 @@ void RenderText::Draw(Canvas* canvas) {
EnsureLayout();
}
+ gfx::Rect clip_rect(display_rect());
+ clip_rect.Inset(ShadowValue::GetMargin(text_shadows_));
+
canvas->Save();
- canvas->ClipRect(display_rect());
+ canvas->ClipRect(clip_rect);
if (!text().empty())
DrawSelection(canvas);
@@ -608,6 +620,10 @@ SelectionModel RenderText::GetSelectionModelForSelectionStart() {
sel.is_reversed() ? CURSOR_BACKWARD : CURSOR_FORWARD);
}
+void RenderText::SetTextShadows(const std::vector<ShadowValue>& shadows) {
+ text_shadows_ = shadows;
+}
+
RenderText::RenderText()
: horizontal_alignment_(base::i18n::IsRTL() ? ALIGN_RIGHT : ALIGN_LEFT),
cursor_enabled_(true),
@@ -788,6 +804,46 @@ void RenderText::ApplyFadeEffects(internal::SkiaTextRenderer* renderer) {
}
}
+void RenderText::ApplyTextShadows(internal::SkiaTextRenderer* renderer) {
+ if (text_shadows_.empty()) {
+ renderer->SetDrawLooper(NULL);
+ return;
+ }
+
+ SkLayerDrawLooper* looper = new SkLayerDrawLooper;
+ SkAutoUnref auto_unref(looper);
+
+ looper->addLayer(); // top layer of the original.
+
+ SkLayerDrawLooper::LayerInfo layer_info;
+ layer_info.fPaintBits |= SkLayerDrawLooper::kMaskFilter_Bit;
+ layer_info.fPaintBits |= SkLayerDrawLooper::kColorFilter_Bit;
+ layer_info.fColorMode = SkXfermode::kSrc_Mode;
+
+ for (size_t i = 0; i < text_shadows_.size(); ++i) {
+ const ShadowValue& shadow = text_shadows_[i];
+
+ layer_info.fOffset.set(SkIntToScalar(shadow.x()),
+ SkIntToScalar(shadow.y()));
+
+ // SkBlurMaskFilter's blur radius defines the range to extend the blur from
+ // original mask, which is half of blur amount as defined in ShadowValue.
+ SkMaskFilter* blur_mask = SkBlurMaskFilter::Create(
+ SkDoubleToScalar(shadow.blur() / 2),
+ SkBlurMaskFilter::kNormal_BlurStyle,
+ SkBlurMaskFilter::kHighQuality_BlurFlag);
+ SkColorFilter* color_filter = SkColorFilter::CreateModeFilter(
+ shadow.color(),
+ SkXfermode::kSrcIn_Mode);
+
+ SkPaint* paint = looper->addLayer(layer_info);
+ SkSafeUnref(paint->setMaskFilter(blur_mask));
+ SkSafeUnref(paint->setColorFilter(color_filter));
+ }
+
+ renderer->SetDrawLooper(looper);
+}
+
// static
bool RenderText::RangeContainsCaret(const ui::Range& range,
size_t caret_pos,
diff --git a/ui/gfx/render_text.h b/ui/gfx/render_text.h
index 1e32489..8077828 100644
--- a/ui/gfx/render_text.h
+++ b/ui/gfx/render_text.h
@@ -28,6 +28,7 @@ namespace gfx {
class Canvas;
class RenderTextTest;
+class ShadowValue;
struct StyleRange;
namespace internal {
@@ -38,6 +39,7 @@ class SkiaTextRenderer {
explicit SkiaTextRenderer(Canvas* canvas);
~SkiaTextRenderer();
+ void SetDrawLooper(SkDrawLooper* draw_looper);
void SetFontSmoothingSettings(bool enable_smoothing, bool enable_lcd_text);
void SetTypeface(SkTypeface* typeface);
void SetTextSize(int size);
@@ -214,6 +216,8 @@ class UI_EXPORT RenderText {
// Get the size in pixels of the entire string. For the height, this will
// return the maximum height among the different fonts in the text runs.
+ // Note that this returns the raw size of the string, which does not include
+ // the margin area of text shadows.
virtual Size GetStringSize() = 0;
void Draw(Canvas* canvas);
@@ -246,6 +250,9 @@ class UI_EXPORT RenderText {
// The returned value represents a cursor/caret position without a selection.
SelectionModel GetSelectionModelForSelectionStart();
+ // Sets shadows to drawn with text.
+ void SetTextShadows(const std::vector<ShadowValue>& shadows);
+
protected:
RenderText();
@@ -339,6 +346,9 @@ class UI_EXPORT RenderText {
// Applies fade effects to |renderer|.
void ApplyFadeEffects(internal::SkiaTextRenderer* renderer);
+ // Applies text shadows to |renderer|.
+ void ApplyTextShadows(internal::SkiaTextRenderer* renderer);
+
// A convenience function to check whether the glyph attached to the caret
// is within the given range.
static bool RangeContainsCaret(const ui::Range& range,
@@ -430,6 +440,9 @@ class UI_EXPORT RenderText {
// selection, font, and other operations that adjust the visible text bounds.
bool cached_bounds_and_offset_valid_;
+ // Text shadows to be drawn.
+ std::vector<ShadowValue> text_shadows_;
+
DISALLOW_COPY_AND_ASSIGN(RenderText);
};
diff --git a/ui/gfx/render_text_linux.cc b/ui/gfx/render_text_linux.cc
index e411cd9..6333bba 100644
--- a/ui/gfx/render_text_linux.cc
+++ b/ui/gfx/render_text_linux.cc
@@ -385,6 +385,7 @@ void RenderTextLinux::DrawVisualText(Canvas* canvas) {
internal::SkiaTextRenderer renderer(canvas);
ApplyFadeEffects(&renderer);
+ ApplyTextShadows(&renderer);
renderer.SetFontSmoothingSettings(
true /* enable_smoothing */,
IsSubpixelRenderingEnabledInFontConfig() && !background_is_transparent());
diff --git a/ui/gfx/render_text_win.cc b/ui/gfx/render_text_win.cc
index cee9738..470a499 100644
--- a/ui/gfx/render_text_win.cc
+++ b/ui/gfx/render_text_win.cc
@@ -476,6 +476,7 @@ void RenderTextWin::DrawVisualText(Canvas* canvas) {
internal::SkiaTextRenderer renderer(canvas);
ApplyFadeEffects(&renderer);
+ ApplyTextShadows(&renderer);
bool smoothing_enabled;
bool cleartype_enabled;
diff --git a/ui/gfx/shadow_value.cc b/ui/gfx/shadow_value.cc
new file mode 100644
index 0000000..4df5013
--- /dev/null
+++ b/ui/gfx/shadow_value.cc
@@ -0,0 +1,61 @@
+// 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/shadow_value.h"
+
+#include "base/stringprintf.h"
+#include "ui/gfx/insets.h"
+
+namespace gfx {
+
+ShadowValue::ShadowValue()
+ : blur_(0),
+ color_(0) {
+}
+
+ShadowValue::ShadowValue(const gfx::Point& offset,
+ double blur,
+ SkColor color)
+ : offset_(offset),
+ blur_(blur),
+ color_(color) {
+}
+
+ShadowValue::~ShadowValue() {
+}
+
+std::string ShadowValue::ToString() const {
+ return base::StringPrintf(
+ "(%d,%d),%.2f,rgba(%d,%d,%d,%d)",
+ offset_.x(), offset_.y(),
+ blur_,
+ SkColorGetR(color_),
+ SkColorGetG(color_),
+ SkColorGetB(color_),
+ SkColorGetA(color_));
+}
+
+// static
+Insets ShadowValue::GetMargin(const std::vector<ShadowValue>& shadows) {
+ int left = 0;
+ int top = 0;
+ int right = 0;
+ int bottom = 0;
+
+ for (size_t i = 0; i < shadows.size(); ++i) {
+ const ShadowValue& shadow = shadows[i];
+
+ // Add 0.5 to round up to the next integer.
+ int blur = static_cast<int>(shadow.blur() / 2 + 0.5);
+
+ left = std::max(left, blur - shadow.x());
+ top = std::max(top, blur - shadow.y());
+ right = std::max(right, blur + shadow.x());
+ bottom = std::max(bottom, blur + shadow.y());
+ }
+
+ return Insets(-top, -left, -bottom, -right);
+}
+
+} // namespace gfx
diff --git a/ui/gfx/shadow_value.h b/ui/gfx/shadow_value.h
new file mode 100644
index 0000000..8ca9196
--- /dev/null
+++ b/ui/gfx/shadow_value.h
@@ -0,0 +1,58 @@
+// 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_SHADOW_VALUE_
+#define UI_GFX_SHADOW_VALUE_
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include "third_party/skia/include/core/SkColor.h"
+#include "ui/base/ui_export.h"
+#include "ui/gfx/point.h"
+
+namespace gfx {
+
+class Insets;
+
+// ShadowValue encapsulates parameters needed to define a shadow, including the
+// shadow's offset, blur amount and color.
+class UI_EXPORT ShadowValue {
+ public:
+ ShadowValue();
+ ShadowValue(const gfx::Point& offset, double blur, SkColor color);
+ ~ShadowValue();
+
+ int x() const { return offset_.x(); }
+ int y() const { return offset_.y(); }
+ const gfx::Point& offset() const { return offset_; }
+ double blur() const { return blur_; }
+ SkColor color() const { return color_; }
+
+ std::string ToString() const;
+
+ // Gets margin space needed for shadows. Note that values in returned Insets
+ // are negative because shadow margins are outside a boundary.
+ static Insets GetMargin(const std::vector<ShadowValue>& shadows);
+
+ private:
+ gfx::Point offset_;
+
+ // Blur amount of the shadow in pixels. If underlying implementation supports
+ // (e.g. Skia), it can have fraction part such as 0.5 pixel. The value
+ // defines a range from full shadow color at the start point inside the
+ // shadow to fully transparent at the end point outside it. The range is
+ // perpendicular to and centered on the shadow edge. For example, a blur
+ // amount of 4.0 means to have a blurry shadow edge of 4 pixels that
+ // transitions from full shadow color to fully transparent and with 2 pixels
+ // inside the shadow and 2 pixels goes beyond the edge.
+ double blur_;
+
+ SkColor color_;
+};
+
+} // namespace gfx
+
+#endif // UI_GFX_SHADOW_VALUE_
diff --git a/ui/gfx/shadow_value_unittest.cc b/ui/gfx/shadow_value_unittest.cc
new file mode 100644
index 0000000..8c30b2b
--- /dev/null
+++ b/ui/gfx/shadow_value_unittest.cc
@@ -0,0 +1,65 @@
+// 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 "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/insets.h"
+#include "ui/gfx/shadow_value.h"
+
+namespace gfx {
+
+TEST(ShadowValueTest, GetMargin) {
+ const struct TestCase {
+ Insets expected_margin;
+ size_t shadow_count;
+ ShadowValue shadows[2];
+ } kTestCases[] = {
+ {
+ Insets(), 0, {},
+ },
+ {
+ Insets(-2, -2, -2, -2),
+ 1,
+ { ShadowValue(gfx::Point(0, 0), 4, 0), },
+ },
+ {
+ Insets(0, -1, -4, -3),
+ 1,
+ { ShadowValue(gfx::Point(1, 2), 4, 0), },
+ },
+ {
+ Insets(-4, -3, 0, -1),
+ 1,
+ { ShadowValue(gfx::Point(-1, -2), 4, 0), },
+ },
+ {
+ Insets(0, -1, -5, -4),
+ 2,
+ {
+ ShadowValue(gfx::Point(1, 2), 4, 0),
+ ShadowValue(gfx::Point(2, 3), 4, 0),
+ },
+ },
+ {
+ Insets(-4, -3, -5, -4),
+ 2,
+ {
+ ShadowValue(gfx::Point(-1, -2), 4, 0),
+ ShadowValue(gfx::Point(2, 3), 4, 0),
+ },
+ },
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestCases); ++i) {
+ std::vector<ShadowValue> shadows(
+ &kTestCases[i].shadows[0],
+ &kTestCases[i].shadows[kTestCases[i].shadow_count]);
+
+ Insets margin = ShadowValue::GetMargin(shadows);
+
+ EXPECT_EQ(kTestCases[i].expected_margin, margin) << " i=" << i;
+ }
+}
+
+} // namespace gfx
diff --git a/ui/ui.gyp b/ui/ui.gyp
index 6606033..1820dd8 100644
--- a/ui/ui.gyp
+++ b/ui/ui.gyp
@@ -368,6 +368,8 @@
'gfx/scrollbar_size.h',
'gfx/selection_model.cc',
'gfx/selection_model.h',
+ 'gfx/shadow_value.cc',
+ 'gfx/shadow_value.h',
'gfx/size.cc',
'gfx/size.h',
'gfx/skbitmap_operations.cc',
diff --git a/ui/ui_unittests.gypi b/ui/ui_unittests.gypi
index afafb6e..d68c9c7 100644
--- a/ui/ui_unittests.gypi
+++ b/ui/ui_unittests.gypi
@@ -91,6 +91,7 @@
'gfx/insets_unittest.cc',
'gfx/rect_unittest.cc',
'gfx/screen_unittest.cc',
+ 'gfx/shadow_value_unittest.cc',
'gfx/skbitmap_operations_unittest.cc',
'gfx/skia_util_unittest.cc',
'gfx/transform_util_unittest.cc',