diff options
author | sail@chromium.org <sail@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-04-05 06:07:56 +0000 |
---|---|---|
committer | sail@chromium.org <sail@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-04-05 06:07:56 +0000 |
commit | 189b01cd199726bd100fcd66fcbaee4f898c04a4 (patch) | |
tree | 0435b8dbf4af0444b47a8d3368eca7cf67302175 /ui | |
parent | 429f687ce523db9114bd6284865ecf4d0737322e (diff) | |
download | chromium_src-189b01cd199726bd100fcd66fcbaee4f898c04a4.zip chromium_src-189b01cd199726bd100fcd66fcbaee4f898c04a4.tar.gz chromium_src-189b01cd199726bd100fcd66fcbaee4f898c04a4.tar.bz2 |
Fade tab titles
This change fades tab titles instead of truncating them with a "...".
I tried to copy the Mac implemenation from here:
http://code.google.com/p/google-toolbox-for-mac/source/browse/trunk/AppKit/GTMFadeTruncatingTextFieldCell.m
but my version is not as good since I couldn't figure out how to draw text on a transparent background.
BUG=69301
TEST=Ran Chromium and verified that things looked ok.
Review URL: http://codereview.chromium.org/6695043
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@80416 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ui')
-rw-r--r-- | ui/gfx/canvas_skia.h | 19 | ||||
-rw-r--r-- | ui/gfx/canvas_skia_win.cc | 277 |
2 files changed, 292 insertions, 4 deletions
diff --git a/ui/gfx/canvas_skia.h b/ui/gfx/canvas_skia.h index 9516e8a3..4811a81 100644 --- a/ui/gfx/canvas_skia.h +++ b/ui/gfx/canvas_skia.h @@ -37,6 +37,12 @@ class Canvas; class CanvasSkia : public skia::PlatformCanvas, public Canvas { public: + enum TruncateFadeMode { + TruncateFadeTail, + TruncateFadeHead, + TruncateFadeHeadAndTail, + }; + // Creates an empty Canvas. Callers must use initialize before using the // canvas. CanvasSkia(); @@ -132,6 +138,19 @@ class CanvasSkia : public skia::PlatformCanvas, const SkColor& color, int x, int y, int w, int h, int flags); +#if defined(OS_WIN) + // Draws the given string with the beginning and/or the end using a fade + // gradient. When truncating the head + // |desired_characters_to_truncate_from_head| specifies the maximum number of + // characters that can be truncated. + virtual void DrawFadeTruncatingString( + const string16& text, + TruncateFadeMode truncate_mode, + size_t desired_characters_to_truncate_from_head, + const gfx::Font& font, + const SkColor& color, + const gfx::Rect& display_rect); +#endif virtual void DrawFocusRect(int x, int y, int width, int height); virtual void TileImageInt(const SkBitmap& bitmap, int x, int y, int w, int h); virtual void TileImageInt(const SkBitmap& bitmap, diff --git a/ui/gfx/canvas_skia_win.cc b/ui/gfx/canvas_skia_win.cc index b343234..c498bb6 100644 --- a/ui/gfx/canvas_skia_win.cc +++ b/ui/gfx/canvas_skia_win.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// 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. @@ -7,12 +7,21 @@ #include <limits> #include "base/i18n/rtl.h" +#include "base/logging.h" +#include "base/scoped_ptr.h" +#include "skia/ext/bitmap_platform_device.h" +#include "skia/ext/skia_utils_win.h" #include "third_party/skia/include/core/SkShader.h" #include "ui/gfx/font.h" #include "ui/gfx/rect.h" namespace { +static inline int Round(double x) { + // Why oh why is this not in a standard header? + return static_cast<int>(floor(x + 0.5)); +} + // We make sure that LTR text we draw in an RTL context is modified // appropriately to make sure it maintains it LTR orientation. void DoDrawText(HDC hdc, @@ -124,6 +133,136 @@ int ComputeFormatFlags(int flags, const string16& text) { return f; } +// Changes the alpha of the given bitmap. +// If |fade_to_right| is true then the rect fades from opaque to clear, +// otherwise the rect fades from clear to opaque. +void FadeBitmapRect(skia::BitmapPlatformDevice& bmp_device, + const gfx::Rect& rect, + bool fade_to_right) { + SkBitmap bmp = bmp_device.accessBitmap(true); + DCHECK_EQ(SkBitmap::kARGB_8888_Config, bmp.config()); + SkAutoLockPixels lock(bmp); + float total_width = static_cast<float>(rect.width()); + + for (int x = rect.x(); x < rect.right(); x++) { + float cur_width = static_cast<float>(fade_to_right ? + rect.right() - x : x - rect.x()); + // We want the fade effect to go from 0.2 to 1.0. + float alpha_percent = ((cur_width / total_width) * 0.8f) + 0.2f; + + for (int y = rect.y(); y < rect.bottom(); y++) { + SkColor color = bmp.getColor(x, y); + SkAlpha alpha = static_cast<SkAlpha>(SkColorGetA(color) * alpha_percent); + *bmp.getAddr32(x, y) = SkPreMultiplyColor(SkColorSetA(color, alpha)); + } + } +} + +// DrawText() doesn't support alpha channels. To create a transparent background +// this function draws black on white. It then uses the intensity of black +// to determine how much alpha to use. The text is drawn in |gfx_text_rect| and +// clipped to |gfx_draw_rect|. +void DrawTextAndClearBackground(skia::BitmapPlatformDevice& bmp_device, + HFONT font, + COLORREF text_color, + const string16& text, + int flags, + const gfx::Rect& gfx_text_rect, + const gfx::Rect& gfx_draw_rect) { + // Clear the background by filling with white. + HBRUSH fill_brush = static_cast<HBRUSH>(GetStockObject(WHITE_BRUSH)); + HANDLE old_brush = SelectObject(bmp_device.getBitmapDC(), fill_brush); + RECT draw_rect = gfx_draw_rect.ToRECT(); + FillRect(bmp_device.getBitmapDC(), &draw_rect, fill_brush); + SelectObject(bmp_device.getBitmapDC(), old_brush); + + // Set black text with trasparent background. + SetBkMode(bmp_device.getBitmapDC(), TRANSPARENT); + SetTextColor(bmp_device.getBitmapDC(), 0); + + // Draw the text. + int save_dc_id = SaveDC(bmp_device.getBitmapDC()); + // Clip the text to the draw destination. + IntersectClipRect(bmp_device.getBitmapDC(), draw_rect.left, draw_rect.top, + draw_rect.right, draw_rect.bottom); + SelectObject(bmp_device.getBitmapDC(), font); + RECT text_rect = gfx_text_rect.ToRECT(); + DoDrawText(bmp_device.getBitmapDC(), text, &text_rect, + ComputeFormatFlags(flags, text)); + RestoreDC(bmp_device.getBitmapDC(), save_dc_id); + + BYTE text_color_r = GetRValue(text_color); + BYTE text_color_g = GetGValue(text_color); + BYTE text_color_b = GetBValue(text_color); + + SkBitmap bmp = bmp_device.accessBitmap(true); + DCHECK_EQ(SkBitmap::kARGB_8888_Config, bmp.config()); + SkAutoLockPixels lock(bmp); + + // At this point the bitmap has black text on white. + // The intensity of black tells us the alpha value of the text. + for (int y = draw_rect.top; y < draw_rect.bottom; y++) { + for (int x = draw_rect.left; x < draw_rect.right; x++) { + // Gets the color directly. DrawText doesn't premultiply alpha so + // using SkBitmap::getColor() won't work here. + SkColor color = *bmp.getAddr32(x, y); + // Use any of the color channels as alpha. + BYTE alpha = 0xFF - SkColorGetB(color); + *bmp.getAddr32(x, y) = SkPreMultiplyColor( + SkColorSetARGB(alpha, text_color_r, text_color_b, text_color_g)); + } + } +} + +// Draws the given text with a fade out gradient. |bmp_device| is a bitmap +// that is used to temporary drawing. The text is drawn in |text_rect| and +// clipped to |draw_rect|. +void DrawTextGradientPart(HDC hdc, + skia::BitmapPlatformDevice& bmp_device, + const string16& text, + const SkColor& color, + HFONT font, + const gfx::Rect& text_rect, + const gfx::Rect& draw_rect, + bool fade_to_right, + int flags) { + DrawTextAndClearBackground(bmp_device, font, skia::SkColorToCOLORREF(color), + text, flags, text_rect, draw_rect); + FadeBitmapRect(bmp_device, draw_rect, fade_to_right); + BLENDFUNCTION blend = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA}; + AlphaBlend(hdc, draw_rect.x(), draw_rect.y(), draw_rect.width(), + draw_rect.height(), bmp_device.getBitmapDC(), draw_rect.x(), + draw_rect.y(), draw_rect.width(), draw_rect.height(), blend); +} + +enum PrimarySide { + PrimaryOnLeft, + PrimaryOnRight, +}; + +// Divides |rect| horizontally into a |primary| of width |primary_width| and a +// |secondary| taking up the remainder. +void DivideRect(const gfx::Rect& rect, + PrimarySide primary_side, + int primary_width, + gfx::Rect* primary, + gfx::Rect* secondary) { + *primary = rect; + *secondary = rect; + int remainder = rect.width() - primary_width; + + switch (primary_side) { + case PrimaryOnLeft: + primary->Inset(0, 0, remainder, 0); + secondary->Inset(primary_width, 0, 0, 0); + break; + case PrimaryOnRight: + primary->Inset(remainder, 0, 0, 0); + secondary->Inset(0, 0, primary_width, 0); + break; + } +} + } // anonymous namespace namespace gfx { @@ -182,7 +321,13 @@ void CanvasSkia::DrawStringInt(const string16& text, const SkColor& color, int x, int y, int w, int h, int flags) { - if (!IntersectsClipRectInt(x, y, w, h)) + SkRect fclip; + if (!getClipBounds(&fclip)) + return; + RECT text_bounds = { x, y, x + w, y + h }; + SkIRect clip; + fclip.round(&clip); + if (!clip.intersect(skia::RECTToSkIRect(text_bounds))) return; // Clamp the max amount of text we'll draw to 32K. There seem to be bugs in @@ -192,7 +337,6 @@ void CanvasSkia::DrawStringInt(const string16& text, const int kMaxStringLength = 32768 - 1; // So the trailing \0 fits in 32K. string16 clamped_string(text.substr(0, kMaxStringLength)); - RECT text_bounds = { x, y, x + w, y + h }; HDC dc = beginPlatformPaint(); SetBkMode(dc, TRANSPARENT); HFONT old_font = (HFONT)SelectObject(dc, font); @@ -211,7 +355,8 @@ void CanvasSkia::DrawStringInt(const string16& text, // Windows will have cleared the alpha channel of the text we drew. Assume // we're drawing to an opaque surface, or at least the text rect area is // opaque. - getTopPlatformDevice().makeOpaque(x, y, w, h); + getTopPlatformDevice().makeOpaque(clip.fLeft, clip.fTop, + clip.width(), clip.height()); } void CanvasSkia::DrawStringInt(const string16& text, @@ -303,4 +448,128 @@ ui::TextureID CanvasSkia::GetTextureID() { return 0; } +void CanvasSkia::DrawFadeTruncatingString( + const string16& text, + CanvasSkia::TruncateFadeMode truncate_mode, + size_t desired_characters_to_truncate_from_head, + const gfx::Font& font, + const SkColor& color, + const gfx::Rect& display_rect) { + int flags = NO_ELLIPSIS; + + // If the whole string fits in the destination then just draw it directly. + int total_string_width; + int total_string_height; + SizeStringInt(text, font, &total_string_width, &total_string_height, + flags | TEXT_VALIGN_TOP); + if (total_string_width <= display_rect.width()) { + DrawStringInt(text, font, color, display_rect.x(), display_rect.y(), + display_rect.width(), display_rect.height(), flags); + return; + } + + int average_character_width = font.GetAverageCharacterWidth(); + int clipped_string_width = total_string_width - display_rect.width(); + + // Clip the string by drawing it to the left by |offset_x|. + int offset_x = 0; + switch (truncate_mode) { + case TruncateFadeHead: + offset_x = clipped_string_width; + break; + case TruncateFadeHeadAndTail: + DCHECK_GT(desired_characters_to_truncate_from_head, 0u); + // Get the width of the beginning of the string we're clipping. + string16 clipped_head_string = + text.substr(0, desired_characters_to_truncate_from_head); + int clipped_width; + int clipped_height; + SizeStringInt(clipped_head_string, font, + &clipped_width, &clipped_height, flags); + + // This is the offset at which we start drawing. This causes the + // beginning of the string to get clipped. + offset_x = clipped_width; + + // Due to the fade effect the first character is hard to see. + // We want to make sure that the first character starting at + // |desired_characters_to_truncate_from_head| is readable so we reduce + // the offset by a little bit. + offset_x = std::max(0, Round(offset_x - average_character_width * 2)); + + // If the offset is so large that there's empty space at the tail + // then reduce the offset so we can use up the empty space. + offset_x = std::min(offset_x, clipped_string_width); + break; + } + bool is_truncating_head = offset_x > 0; + bool is_truncating_tail = clipped_string_width > offset_x; + + bool is_rtl = (ComputeFormatFlags(flags, text) & DT_RTLREADING) != 0; + // |is_rtl| tells us if the given text is right to left or not. |is_rtl| can + // be false even if the UI is set to a right to left language. + // Now, normally, we right align all text if the UI is set to a right to + // left language. In this case though we don't want that because we render + // the ends of the string ourselves. + if (!is_rtl) + flags |= TEXT_ALIGN_LEFT; + + // 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 string is very short. + int gradient_width = Round(std::min(average_character_width * 2.5, + display_rect.width() / 4.0)); + + // Move the origin to |display_rect.origin()|. This simplifies all the + // drawing so that both the source and destination can be (0,0). + save(kMatrix_SaveFlag); + TranslateInt(display_rect.x(), display_rect.y()); + + gfx::Rect solid_part(gfx::Point(), display_rect.size()); + gfx::Rect head_part; + gfx::Rect tail_part; + if (is_truncating_head) + DivideRect(solid_part, is_rtl ? PrimaryOnRight : PrimaryOnLeft, + gradient_width, &head_part, &solid_part); + if (is_truncating_tail) + DivideRect(solid_part, is_rtl ? PrimaryOnLeft : PrimaryOnRight, + gradient_width, &tail_part, &solid_part); + + // Grow |display_rect| by |offset_x|. + gfx::Rect text_rect(gfx::Point(), display_rect.size()); + if (!is_rtl) + text_rect.set_x(text_rect.x() - offset_x); + text_rect.set_width(text_rect.width() + offset_x); + + // Disable clear type. This makes the text render in gray scale which + // makes it easier to compute the alpha value. + LOGFONT font_info; + GetObject(font.GetNativeFont(), sizeof(font_info), &font_info); + font_info.lfQuality = ANTIALIASED_QUALITY; + HFONT gray_scale_font = CreateFontIndirect(&font_info); + + HDC hdc = beginPlatformPaint(); + scoped_ptr<skia::BitmapPlatformDevice> gradient_bitmap( + skia::BitmapPlatformDevice::create(NULL, display_rect.width(), + display_rect.height(), false, NULL)); + if (is_truncating_head) + DrawTextGradientPart(hdc, *gradient_bitmap, text, color, gray_scale_font, + text_rect, head_part, is_rtl, flags); + if (is_truncating_tail) + DrawTextGradientPart(hdc, *gradient_bitmap, text, color, gray_scale_font, + text_rect, tail_part, !is_rtl, flags); + endPlatformPaint(); + + // Draw the solid part. + save(kClip_SaveFlag); + ClipRectInt(solid_part.x(), solid_part.y(), + solid_part.width(), solid_part.height()); + DrawStringInt(text, font, color, + text_rect.x(), text_rect.y(), + text_rect.width(), text_rect.height(), + flags); + restore(); + restore(); +} + } // namespace gfx |