summaryrefslogtreecommitdiffstats
path: root/gfx
diff options
context:
space:
mode:
authorben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-23 04:05:01 +0000
committerben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-23 04:05:01 +0000
commitd07b6b5b992faad3506ac004503c11d25930cdca (patch)
treea5a795b8980d90e4a10208ad4f8eabe140ee3e5e /gfx
parentf2a13d89ca22cdbe3808bcb14e02d5baee8d33b3 (diff)
downloadchromium_src-d07b6b5b992faad3506ac004503c11d25930cdca.zip
chromium_src-d07b6b5b992faad3506ac004503c11d25930cdca.tar.gz
chromium_src-d07b6b5b992faad3506ac004503c11d25930cdca.tar.bz2
Move app/gfx/canvas and app/gfx/font to gfx/.
TBR=darin BUG=none TEST=none Review URL: http://codereview.chromium.org/1132006 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@42312 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'gfx')
-rw-r--r--gfx/canvas.cc277
-rw-r--r--gfx/canvas.h238
-rw-r--r--gfx/canvas_linux.cc281
-rw-r--r--gfx/canvas_mac.mm78
-rw-r--r--gfx/canvas_paint.h20
-rw-r--r--gfx/canvas_win.cc286
-rw-r--r--gfx/font.h287
-rw-r--r--gfx/font_gtk.cc156
-rw-r--r--gfx/font_mac.mm93
-rw-r--r--gfx/font_skia.cc255
-rw-r--r--gfx/font_unittest.cc80
-rw-r--r--gfx/font_win.cc224
-rw-r--r--gfx/gfx.gyp16
13 files changed, 2291 insertions, 0 deletions
diff --git a/gfx/canvas.cc b/gfx/canvas.cc
new file mode 100644
index 0000000..0099e4c
--- /dev/null
+++ b/gfx/canvas.cc
@@ -0,0 +1,277 @@
+// Copyright (c) 2006-2008 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 "gfx/canvas.h"
+
+#include <limits>
+
+#include "base/i18n/rtl.h"
+#include "base/logging.h"
+#include "gfx/font.h"
+#include "gfx/rect.h"
+#include "third_party/skia/include/core/SkShader.h"
+
+namespace gfx {
+
+bool Canvas::GetClipRect(gfx::Rect* r) {
+ SkRect clip;
+ if (!getClipBounds(&clip)) {
+ if (r)
+ r->SetRect(0, 0, 0, 0);
+ return false;
+ }
+ r->SetRect(SkScalarRound(clip.fLeft), SkScalarRound(clip.fTop),
+ SkScalarRound(clip.fRight - clip.fLeft),
+ SkScalarRound(clip.fBottom - clip.fTop));
+ return true;
+}
+
+bool Canvas::ClipRectInt(int x, int y, int w, int h) {
+ SkRect new_clip;
+ new_clip.set(SkIntToScalar(x), SkIntToScalar(y),
+ SkIntToScalar(x + w), SkIntToScalar(y + h));
+ return clipRect(new_clip);
+}
+
+bool Canvas::IntersectsClipRectInt(int x, int y, int w, int h) {
+ SkRect clip;
+ return getClipBounds(&clip) &&
+ clip.intersect(SkIntToScalar(x), SkIntToScalar(y), SkIntToScalar(x + w),
+ SkIntToScalar(y + h));
+}
+
+void Canvas::TranslateInt(int x, int y) {
+ translate(SkIntToScalar(x), SkIntToScalar(y));
+}
+
+void Canvas::ScaleInt(int x, int y) {
+ scale(SkIntToScalar(x), SkIntToScalar(y));
+}
+
+void Canvas::FillRectInt(const SkColor& color, int x, int y, int w, int h) {
+ SkPaint paint;
+ paint.setColor(color);
+ paint.setStyle(SkPaint::kFill_Style);
+ paint.setXfermodeMode(SkXfermode::kSrcOver_Mode);
+ FillRectInt(x, y, w, h, paint);
+}
+
+void Canvas::FillRectInt(int x, int y, int w, int h, const SkPaint& paint) {
+ SkIRect rc = {x, y, x + w, y + h};
+ drawIRect(rc, paint);
+}
+
+void Canvas::DrawRectInt(const SkColor& color, int x, int y, int w, int h) {
+ DrawRectInt(color, x, y, w, h, SkXfermode::kSrcOver_Mode);
+}
+
+void Canvas::DrawRectInt(const SkColor& color, int x, int y, int w, int h,
+ SkXfermode::Mode mode) {
+ SkPaint paint;
+ paint.setColor(color);
+ paint.setStyle(SkPaint::kStroke_Style);
+ // Set a stroke width of 0, which will put us down the stroke rect path. If
+ // we set a stroke width of 1, for example, this will internally create a
+ // path and fill it, which causes problems near the edge of the canvas.
+ paint.setStrokeWidth(SkIntToScalar(0));
+ paint.setXfermodeMode(mode);
+
+ SkIRect rc = {x, y, x + w, y + h};
+ drawIRect(rc, paint);
+}
+
+void Canvas::DrawLineInt(const SkColor& color, int x1, int y1, int x2, int y2) {
+ SkPaint paint;
+ paint.setColor(color);
+ paint.setStrokeWidth(SkIntToScalar(1));
+ drawLine(SkIntToScalar(x1), SkIntToScalar(y1), SkIntToScalar(x2),
+ SkIntToScalar(y2), paint);
+}
+
+void Canvas::DrawFocusRect(int x, int y, int width, int height) {
+ // Create a 2D bitmap containing alternating on/off pixels - we do this
+ // so that you never get two pixels of the same color around the edges
+ // of the focus rect (this may mean that opposing edges of the rect may
+ // have a dot pattern out of phase to each other).
+ static SkBitmap* dots = NULL;
+ if (!dots) {
+ int col_pixels = 32;
+ int row_pixels = 32;
+
+ dots = new SkBitmap;
+ dots->setConfig(SkBitmap::kARGB_8888_Config, col_pixels, row_pixels);
+ dots->allocPixels();
+ dots->eraseARGB(0, 0, 0, 0);
+
+ uint32_t* dot = dots->getAddr32(0, 0);
+ for (int i = 0; i < row_pixels; i++) {
+ for (int u = 0; u < col_pixels; u++) {
+ if ((u % 2 + i % 2) % 2 != 0) {
+ dot[i * row_pixels + u] = SK_ColorGRAY;
+ }
+ }
+ }
+ }
+
+ // First the horizontal lines.
+
+ // Make a shader for the bitmap with an origin of the box we'll draw. This
+ // shader is refcounted and will have an initial refcount of 1.
+ SkShader* shader = SkShader::CreateBitmapShader(
+ *dots, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode);
+ // Assign the shader to the paint & release our reference. The paint will
+ // now own the shader and the shader will be destroyed when the paint goes
+ // out of scope.
+ SkPaint paint;
+ paint.setShader(shader);
+ shader->unref();
+
+ SkRect rect;
+ rect.set(SkIntToScalar(x), SkIntToScalar(y),
+ SkIntToScalar(x + width), SkIntToScalar(y + 1));
+ drawRect(rect, paint);
+ rect.set(SkIntToScalar(x), SkIntToScalar(y + height - 1),
+ SkIntToScalar(x + width), SkIntToScalar(y + height));
+ drawRect(rect, paint);
+
+ rect.set(SkIntToScalar(x), SkIntToScalar(y),
+ SkIntToScalar(x + 1), SkIntToScalar(y + height));
+ drawRect(rect, paint);
+ rect.set(SkIntToScalar(x + width - 1), SkIntToScalar(y),
+ SkIntToScalar(x + width), SkIntToScalar(y + height));
+ drawRect(rect, paint);
+}
+
+void Canvas::DrawBitmapInt(const SkBitmap& bitmap, int x, int y) {
+ drawBitmap(bitmap, SkIntToScalar(x), SkIntToScalar(y));
+}
+
+void Canvas::DrawBitmapInt(const SkBitmap& bitmap, int x, int y,
+ const SkPaint& paint) {
+ drawBitmap(bitmap, SkIntToScalar(x), SkIntToScalar(y), &paint);
+}
+
+void Canvas::DrawBitmapInt(const SkBitmap& bitmap, 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) {
+ SkPaint p;
+ DrawBitmapInt(bitmap, src_x, src_y, src_w, src_h, dest_x, dest_y,
+ dest_w, dest_h, filter, p);
+}
+
+void Canvas::DrawBitmapInt(const SkBitmap& bitmap, 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) {
+ 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 || dest_w <= 0 || dest_h <= 0) {
+ NOTREACHED() << "Attempting to draw bitmap to/from an empty rect!";
+ return;
+ }
+
+ if (!IntersectsClipRectInt(dest_x, dest_y, dest_w, dest_h))
+ 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) {
+ // 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 };
+ 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.
+ SkShader* shader = SkShader::CreateBitmapShader(bitmap,
+ SkShader::kRepeat_TileMode,
+ SkShader::kRepeat_TileMode);
+ SkMatrix shader_scale;
+ shader_scale.setScale(SkFloatToScalar(static_cast<float>(dest_w) / src_w),
+ SkFloatToScalar(static_cast<float>(dest_h) / src_h));
+ shader_scale.preTranslate(SkIntToScalar(-src_x), SkIntToScalar(-src_y));
+ shader_scale.postTranslate(SkIntToScalar(dest_x), SkIntToScalar(dest_y));
+ shader->setLocalMatrix(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);
+ shader->unref();
+
+ // The rect will be filled by the bitmap.
+ drawRect(dest_rect, p);
+}
+
+void Canvas::DrawStringInt(const std::wstring& text,
+ const gfx::Font& font,
+ const SkColor& color,
+ int x, int y, int w, int h) {
+ DrawStringInt(text, font, color, x, y, w, h,
+ gfx::Canvas::DefaultCanvasTextAlignment());
+}
+
+void Canvas::DrawStringInt(const std::wstring& text,
+ const gfx::Font& font,
+ const SkColor& color,
+ const gfx::Rect& display_rect) {
+ DrawStringInt(text, font, color, display_rect.x(), display_rect.y(),
+ display_rect.width(), display_rect.height());
+}
+
+void Canvas::TileImageInt(const SkBitmap& bitmap, int x, int y, int w, int h) {
+ TileImageInt(bitmap, 0, 0, x, y, w, h);
+}
+
+void Canvas::TileImageInt(const SkBitmap& bitmap, int src_x, int src_y,
+ int dest_x, int dest_y, int w, int h) {
+ if (!IntersectsClipRectInt(dest_x, dest_y, w, h))
+ return;
+
+ SkPaint paint;
+
+ SkShader* shader = SkShader::CreateBitmapShader(bitmap,
+ SkShader::kRepeat_TileMode,
+ SkShader::kRepeat_TileMode);
+ paint.setShader(shader);
+ paint.setXfermodeMode(SkXfermode::kSrcOver_Mode);
+
+ // CreateBitmapShader returns a Shader with a reference count of one, we
+ // need to unref after paint takes ownership of the shader.
+ shader->unref();
+ save();
+ translate(SkIntToScalar(dest_x - src_x), SkIntToScalar(dest_y - src_y));
+ ClipRectInt(src_x, src_y, w, h);
+ drawPaint(paint);
+ restore();
+}
+
+SkBitmap Canvas::ExtractBitmap() const {
+ const SkBitmap& device_bitmap = 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 constuctor, since we want an actual copy
+ // of the bitmap.
+ SkBitmap result;
+ device_bitmap.copyTo(&result, SkBitmap::kARGB_8888_Config);
+ return result;
+}
+
+// static
+int Canvas::DefaultCanvasTextAlignment() {
+ if (!base::i18n::IsRTL())
+ return gfx::Canvas::TEXT_ALIGN_LEFT;
+ return gfx::Canvas::TEXT_ALIGN_RIGHT;
+}
+
+} // namespace gfx
diff --git a/gfx/canvas.h b/gfx/canvas.h
new file mode 100644
index 0000000..ffc30fb
--- /dev/null
+++ b/gfx/canvas.h
@@ -0,0 +1,238 @@
+// Copyright (c) 2006-2008 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 GFX_CANVAS_H_
+#define GFX_CANVAS_H_
+
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "skia/ext/platform_canvas.h"
+
+#if defined(OS_POSIX) && !defined(OS_MACOSX)
+typedef struct _cairo cairo_t;
+typedef struct _GdkPixbuf GdkPixbuf;
+#endif
+
+namespace gfx {
+
+class Font;
+class Rect;
+
+// Canvas is a SkCanvas subclass that provides a number of methods for common
+// operations used throughout an application built using base/gfx and app/gfx.
+//
+// All methods that take integer arguments (as is used throughout views)
+// end with Int. If you need to use methods provided by the superclass
+// you'll need to do a conversion. In particular you'll need to use
+// macro SkIntToScalar(xxx), or if converting from a scalar to an integer
+// SkScalarRound.
+//
+// A handful of methods in this class are overloaded providing an additional
+// argument of type SkXfermode::Mode. SkXfermode::Mode specifies how the
+// source and destination colors are combined. Unless otherwise specified,
+// the variant that does not take a SkXfermode::Mode uses a transfer mode
+// of kSrcOver_Mode.
+class Canvas : public skia::PlatformCanvas {
+ public:
+ // Specifies the alignment for text rendered with the DrawStringInt method.
+ enum {
+ TEXT_ALIGN_LEFT = 1,
+ TEXT_ALIGN_CENTER = 2,
+ TEXT_ALIGN_RIGHT = 4,
+ TEXT_VALIGN_TOP = 8,
+ TEXT_VALIGN_MIDDLE = 16,
+ TEXT_VALIGN_BOTTOM = 32,
+
+ // Specifies the text consists of multiple lines.
+ MULTI_LINE = 64,
+
+ // By default DrawStringInt 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
+ // underline (SHOW_PREFIX), or not rendered at all (HIDE_PREFIX).
+ SHOW_PREFIX = 128,
+ HIDE_PREFIX = 256,
+
+ // Prevent ellipsizing
+ NO_ELLIPSIS = 512,
+
+ // Specifies if words can be split by new lines.
+ // This only works with MULTI_LINE.
+ CHARACTER_BREAK = 1024,
+
+ // Instructs DrawStringInt() 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
+ // directionality characters into strings if the locale is LTR because some
+ // platforms (for example, an English Windows XP with no RTL fonts
+ // installed) don't support these characters. Thus, this flag should be
+ // used to render text using RTL directionality when the locale is LTR.
+ FORCE_RTL_DIRECTIONALITY = 2048,
+ };
+
+ // Creates an empty Canvas. Callers must use initialize before using the
+ // canvas.
+ Canvas();
+
+ Canvas(int width, int height, bool is_opaque);
+
+ virtual ~Canvas();
+
+ // Retrieves the clip rectangle and sets it in the specified rectangle if any.
+ // Returns true if the clip rect is non-empty.
+ bool GetClipRect(gfx::Rect* clip_rect);
+
+ // Wrapper function that takes integer arguments.
+ // Returns true if the clip is non-empty.
+ // See clipRect for specifics.
+ bool ClipRectInt(int x, int y, int w, int h);
+
+ // Test whether the provided rectangle intersects the current clip rect.
+ bool IntersectsClipRectInt(int x, int y, int w, int h);
+
+ // Wrapper function that takes integer arguments.
+ // See translate() for specifics.
+ void TranslateInt(int x, int y);
+
+ // Wrapper function that takes integer arguments.
+ // See scale() for specifics.
+ void ScaleInt(int x, int y);
+
+ // Fills the given rectangle with the given paint's parameters.
+ void FillRectInt(int x, int y, int w, int h, const SkPaint& paint);
+
+ // Fills the specified region with the specified color using a transfer
+ // mode of SkXfermode::kSrcOver_Mode.
+ void FillRectInt(const SkColor& color, int x, int y, int w, int h);
+
+ // Draws a single pixel rect in the specified region with the specified
+ // color, using a transfer mode of SkXfermode::kSrcOver_Mode.
+ //
+ // NOTE: if you need a single pixel line, use DraLineInt.
+ void DrawRectInt(const SkColor& color, int x, int y, int w, int h);
+
+ // Draws a single pixel rect in the specified region with the specified
+ // color and transfer mode.
+ //
+ // NOTE: if you need a single pixel line, use DraLineInt.
+ void DrawRectInt(const SkColor& color, int x, int y, int w, int h,
+ SkXfermode::Mode mode);
+
+ // Draws a single pixel line with the specified color.
+ void DrawLineInt(const SkColor& color, int x1, int y1, int x2, int y2);
+
+ // Draws a bitmap with the origin at the specified location. The upper left
+ // corner of the bitmap is rendered at the specified location.
+ void DrawBitmapInt(const SkBitmap& bitmap, int x, int y);
+
+ // Draws a bitmap with the origin at the specified location, using the
+ // specified paint. The upper left corner of the bitmap is rendered at the
+ // specified location.
+ void DrawBitmapInt(const SkBitmap& bitmap, int x, int y,
+ const SkPaint& paint);
+
+ // Draws a portion of a bitmap in the specified location. The src parameters
+ // correspond to the region of the bitmap to draw in the region defined
+ // by the dest coordinates.
+ //
+ // If the width or height of the source differs from that of the destination,
+ // the bitmap will be scaled. When scaling down, it is highly recommended
+ // that you call buildMipMap(false) on your bitmap to ensure that it has
+ // a mipmap, which will result in much higher-quality output. Set |filter|
+ // to use filtering for bitmaps, otherwise the nearest-neighbor algorithm
+ // is used for resampling.
+ //
+ // An optional custom SkPaint can be provided.
+ void DrawBitmapInt(const SkBitmap& bitmap, 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);
+ void DrawBitmapInt(const SkBitmap& bitmap, 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 text with the specified color, font and location. The text is
+ // aligned to the left, vertically centered, clipped to the region. If the
+ // text is too big, it is truncated and '...' is added to the end.
+ void DrawStringInt(const std::wstring& text, const gfx::Font& font,
+ const SkColor& color, int x, int y, int w, int h);
+ void DrawStringInt(const std::wstring& text, const gfx::Font& font,
+ const SkColor& color, const gfx::Rect& display_rect);
+
+ // Draws text with the specified color, font and location. The last argument
+ // specifies flags for how the text should be rendered. It can be one of
+ // TEXT_ALIGN_CENTER, TEXT_ALIGN_RIGHT or TEXT_ALIGN_LEFT.
+ void DrawStringInt(const std::wstring& text, const gfx::Font& font,
+ const SkColor& color, int x, int y, int w, int h,
+ int flags);
+
+#ifdef OS_WIN // Only implemented on Windows for now.
+ // Draws text with a 1-pixel halo around it of the given color. It allows
+ // ClearType to be drawn to an otherwise transparenct bitmap for drag images.
+ // Drag images have only 1-bit of transparency, so we don't do any fancy
+ // blurring.
+ void DrawStringWithHalo(const std::wstring& text, const gfx::Font& font,
+ const SkColor& text_color, const SkColor& halo_color,
+ int x, int y, int w, int h, int flags);
+#endif
+
+ // Draws a dotted gray rectangle used for focus purposes.
+ void DrawFocusRect(int x, int y, int width, int height);
+
+ // Tiles the image in the specified region.
+ void TileImageInt(const SkBitmap& bitmap, int x, int y, int w, int h);
+ void TileImageInt(const SkBitmap& bitmap, int src_x, int src_y,
+ int dest_x, int dest_y, int w, int h);
+
+ // Extracts a bitmap from the contents of this canvas.
+ SkBitmap ExtractBitmap() const;
+
+#if defined(OS_POSIX) && !defined(OS_MACOSX)
+ // Applies current matrix on the canvas to the cairo context. This should be
+ // invoked anytime you plan on drawing directly to the cairo context. Be
+ // sure and set the matrix back to the identity when done.
+ void ApplySkiaMatrixToCairoContext(cairo_t* cr);
+
+ // Draw the pixbuf in its natural size at (x, y).
+ void DrawGdkPixbuf(GdkPixbuf* pixbuf, int x, int y);
+#endif
+
+ // Compute the size required to draw some text with the provided font.
+ // Attempts to fit the text with the provided width and height. Increases
+ // height and then width as needed to make the text fit. This method
+ // supports multiple lines.
+ static void SizeStringInt(const std::wstring& test, const gfx::Font& font,
+ int *width, int* height, int flags);
+
+ // Returns the default text alignment to be used when drawing text on a
+ // gfx::Canvas based on the directionality of the system locale language. This
+ // function is used by gfx::Canvas::DrawStringInt when the text alignment is
+ // not specified.
+ //
+ // This function returns either gfx::Canvas::TEXT_ALIGN_LEFT or
+ // gfx::Canvas::TEXT_ALIGN_RIGHT.
+ static int DefaultCanvasTextAlignment();
+
+ private:
+#if defined(OS_WIN)
+ // Draws text with the specified color, font and location. The text is
+ // aligned to the left, vertically centered, clipped to the region. If the
+ // text is too big, it is truncated and '...' is added to the end.
+ void DrawStringInt(const std::wstring& text, HFONT font,
+ const SkColor& color, int x, int y, int w, int h,
+ int flags);
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(Canvas);
+};
+
+} // namespace gfx;
+
+#endif // GFX_CANVAS_H_
diff --git a/gfx/canvas_linux.cc b/gfx/canvas_linux.cc
new file mode 100644
index 0000000..0981d56
--- /dev/null
+++ b/gfx/canvas_linux.cc
@@ -0,0 +1,281 @@
+// Copyright (c) 2009 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 "gfx/canvas.h"
+
+#include <cairo/cairo.h>
+#include <gtk/gtk.h>
+#include <pango/pango.h>
+#include <pango/pangocairo.h>
+
+#include "base/logging.h"
+#include "base/utf_string_conversions.h"
+#include "gfx/font.h"
+#include "gfx/rect.h"
+
+namespace {
+
+const gunichar kAcceleratorChar = '&';
+
+// Font settings that we initialize once and then use when drawing text in
+// DrawStringInt().
+static cairo_font_options_t* cairo_font_options = NULL;
+
+// Returns the resolution used by pango. A negative values means the resolution
+// hasn't been set.
+static double GetPangoResolution() {
+ static double resolution;
+ static bool determined_resolution = false;
+ if (!determined_resolution) {
+ determined_resolution = true;
+ PangoContext* default_context = gdk_pango_context_get();
+ resolution = pango_cairo_context_get_resolution(default_context);
+ g_object_unref(default_context);
+ }
+ return resolution;
+}
+
+// Update |cairo_font_options| based on GtkSettings, allocating it if needed.
+static void UpdateCairoFontOptions() {
+ if (!cairo_font_options)
+ cairo_font_options = cairo_font_options_create();
+
+ GtkSettings* gtk_settings = gtk_settings_get_default();
+ gint antialias = 0;
+ gint hinting = 0;
+ gchar* hint_style = NULL;
+ gchar* rgba_style = NULL;
+ g_object_get(gtk_settings,
+ "gtk-xft-antialias", &antialias,
+ "gtk-xft-hinting", &hinting,
+ "gtk-xft-hintstyle", &hint_style,
+ "gtk-xft-rgba", &rgba_style,
+ 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 (hint_style && rgba_style) {
+ if (!antialias) {
+ cairo_font_options_set_antialias(cairo_font_options,
+ CAIRO_ANTIALIAS_NONE);
+ } else if (strcmp(rgba_style, "none") == 0) {
+ cairo_font_options_set_antialias(cairo_font_options,
+ CAIRO_ANTIALIAS_GRAY);
+ } else {
+ cairo_font_options_set_antialias(cairo_font_options,
+ CAIRO_ANTIALIAS_SUBPIXEL);
+ cairo_subpixel_order_t cairo_subpixel_order =
+ CAIRO_SUBPIXEL_ORDER_DEFAULT;
+ if (strcmp(rgba_style, "rgb") == 0) {
+ cairo_subpixel_order = CAIRO_SUBPIXEL_ORDER_RGB;
+ } else if (strcmp(rgba_style, "bgr") == 0) {
+ cairo_subpixel_order = CAIRO_SUBPIXEL_ORDER_BGR;
+ } else if (strcmp(rgba_style, "vrgb") == 0) {
+ cairo_subpixel_order = CAIRO_SUBPIXEL_ORDER_VRGB;
+ } else if (strcmp(rgba_style, "vbgr") == 0) {
+ cairo_subpixel_order = CAIRO_SUBPIXEL_ORDER_VBGR;
+ }
+ cairo_font_options_set_subpixel_order(cairo_font_options,
+ cairo_subpixel_order);
+ }
+
+ cairo_hint_style_t cairo_hint_style = CAIRO_HINT_STYLE_DEFAULT;
+ if (hinting == 0 || strcmp(hint_style, "hintnone") == 0) {
+ cairo_hint_style = CAIRO_HINT_STYLE_NONE;
+ } else if (strcmp(hint_style, "hintslight") == 0) {
+ cairo_hint_style = CAIRO_HINT_STYLE_SLIGHT;
+ } else if (strcmp(hint_style, "hintmedium") == 0) {
+ cairo_hint_style = CAIRO_HINT_STYLE_MEDIUM;
+ } else if (strcmp(hint_style, "hintfull") == 0) {
+ cairo_hint_style = CAIRO_HINT_STYLE_FULL;
+ }
+ cairo_font_options_set_hint_style(cairo_font_options, cairo_hint_style);
+ }
+
+ if (hint_style)
+ g_free(hint_style);
+ if (rgba_style)
+ g_free(rgba_style);
+}
+
+} // namespace
+
+namespace gfx {
+
+Canvas::Canvas(int width, int height, bool is_opaque)
+ : skia::PlatformCanvas(width, height, is_opaque) {
+}
+
+Canvas::Canvas() : skia::PlatformCanvas() {
+}
+
+Canvas::~Canvas() {
+}
+
+// Pass a width > 0 to force wrapping and elliding.
+static void SetupPangoLayout(PangoLayout* layout,
+ const std::wstring& text,
+ const gfx::Font& font,
+ int width,
+ int flags) {
+ if (!cairo_font_options)
+ UpdateCairoFontOptions();
+ // This needs to be done early on; it has no effect when called just before
+ // pango_cairo_show_layout().
+ pango_cairo_context_set_font_options(
+ pango_layout_get_context(layout), cairo_font_options);
+
+ // Callers of DrawStringInt handle RTL layout themselves, so tell pango to not
+ // scope out RTL characters.
+ pango_layout_set_auto_dir(layout, FALSE);
+
+ if (width > 0)
+ pango_layout_set_width(layout, width * PANGO_SCALE);
+
+ if (flags & Canvas::NO_ELLIPSIS) {
+ pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_NONE);
+ } else {
+ pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END);
+ }
+
+ if (flags & Canvas::TEXT_ALIGN_CENTER) {
+ pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
+ } else if (flags & Canvas::TEXT_ALIGN_RIGHT) {
+ pango_layout_set_alignment(layout, PANGO_ALIGN_RIGHT);
+ }
+
+ if (flags & Canvas::MULTI_LINE) {
+ pango_layout_set_wrap(layout,
+ (flags & Canvas::CHARACTER_BREAK) ?
+ PANGO_WRAP_WORD_CHAR : PANGO_WRAP_WORD);
+ }
+
+ // Set the resolution to match that used by Gtk. If we don't set the
+ // resolution and the resolution differs from the default, Gtk and Chrome end
+ // up drawing at different sizes.
+ double resolution = GetPangoResolution();
+ if (resolution > 0) {
+ pango_cairo_context_set_resolution(pango_layout_get_context(layout),
+ resolution);
+ }
+
+ PangoFontDescription* desc = gfx::Font::PangoFontFromGfxFont(font);
+ pango_layout_set_font_description(layout, desc);
+ pango_font_description_free(desc);
+
+ // Set text and accelerator character if needed.
+ std::string utf8 = WideToUTF8(text);
+ if (flags & gfx::Canvas::HIDE_PREFIX) {
+ // Escape the text string to be used as markup.
+ gchar* escaped_text = g_markup_escape_text(utf8.c_str(), utf8.size());
+ pango_layout_set_markup_with_accel(layout,
+ escaped_text,
+ strlen(escaped_text),
+ kAcceleratorChar, NULL);
+ g_free(escaped_text);
+ } else {
+ pango_layout_set_text(layout, utf8.data(), utf8.size());
+ }
+}
+
+// static
+void Canvas::SizeStringInt(const std::wstring& text,
+ const gfx::Font& font,
+ int* width, int* height, int flags) {
+ int org_width = *width;
+ cairo_surface_t* surface =
+ cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 0, 0);
+ cairo_t* cr = cairo_create(surface);
+ PangoLayout* layout = pango_cairo_create_layout(cr);
+
+ SetupPangoLayout(layout, text, font, *width, flags);
+
+ pango_layout_get_pixel_size(layout, width, height);
+
+ if (org_width > 0 && flags & Canvas::MULTI_LINE &&
+ pango_layout_is_wrapped(layout)) {
+ // The text wrapped. There seems to be a bug in Pango when this happens
+ // such that the width returned from pango_layout_get_pixel_size is too
+ // small. Using the width from pango_layout_get_pixel_size in this case
+ // results in wrapping across more lines, which requires a bigger height.
+ // As a workaround we use the original width, which is not necessarily
+ // exactly correct, but isn't wrong by much.
+ //
+ // It looks like Pango uses the size of whitespace in calculating wrapping
+ // but doesn't include the size of the whitespace when the extents are
+ // asked for. See the loop in pango-layout.c process_item that determines
+ // where to wrap.
+ *width = org_width;
+ }
+
+ g_object_unref(layout);
+ cairo_destroy(cr);
+ cairo_surface_destroy(surface);
+}
+
+void Canvas::DrawStringInt(const std::wstring& text,
+ const gfx::Font& font,
+ const SkColor& color,
+ int x, int y, int w, int h,
+ int flags) {
+ if (w <= 0 || h <= 0)
+ return;
+
+ cairo_t* cr = beginPlatformPaint();
+ PangoLayout* layout = pango_cairo_create_layout(cr);
+
+ SetupPangoLayout(layout, text, font, w, flags);
+
+ pango_layout_set_height(layout, h * PANGO_SCALE);
+
+ cairo_save(cr);
+ cairo_set_source_rgb(cr,
+ SkColorGetR(color) / 255.0,
+ SkColorGetG(color) / 255.0,
+ SkColorGetB(color) / 255.0);
+
+ int width, height;
+ pango_layout_get_pixel_size(layout, &width, &height);
+
+ cairo_rectangle(cr, x, y, w, h);
+ cairo_clip(cr);
+
+ if (flags & Canvas::TEXT_VALIGN_TOP) {
+ // Cairo should draw from the top left corner already.
+ } else if (flags & Canvas::TEXT_VALIGN_BOTTOM) {
+ y += (h - height);
+ } else {
+ // Vertically centered.
+ y += ((h - height) / 2);
+ }
+
+ cairo_move_to(cr, x, y);
+ pango_cairo_show_layout(cr, layout);
+ if (font.style() & gfx::Font::UNDERLINED) {
+ double underline_y =
+ static_cast<double>(y) + height + font.underline_position();
+ cairo_set_line_width(cr, font.underline_thickness());
+ cairo_move_to(cr, x, underline_y);
+ cairo_line_to(cr, x + width, underline_y);
+ cairo_stroke(cr);
+ }
+ cairo_restore(cr);
+
+ g_object_unref(layout);
+ // NOTE: beginPlatformPaint returned its surface, we shouldn't destroy it.
+}
+
+void Canvas::DrawGdkPixbuf(GdkPixbuf* pixbuf, int x, int y) {
+ if (!pixbuf) {
+ NOTREACHED();
+ return;
+ }
+
+ cairo_t* cr = beginPlatformPaint();
+ gdk_cairo_set_source_pixbuf(cr, pixbuf, x, y);
+ cairo_paint(cr);
+}
+
+} // namespace gfx
diff --git a/gfx/canvas_mac.mm b/gfx/canvas_mac.mm
new file mode 100644
index 0000000..ebba090
--- /dev/null
+++ b/gfx/canvas_mac.mm
@@ -0,0 +1,78 @@
+// Copyright (c) 2006-2008 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 <Cocoa/Cocoa.h>
+
+#include "gfx/canvas.h"
+
+#include "base/scoped_cftyperef.h"
+#include "base/sys_string_conversions.h"
+#include "gfx/font.h"
+#include "gfx/rect.h"
+#include "third_party/skia/include/core/SkShader.h"
+
+namespace gfx {
+
+Canvas::Canvas(int width, int height, bool is_opaque)
+ : skia::PlatformCanvas(width, height, is_opaque) {
+}
+
+Canvas::Canvas() : skia::PlatformCanvas() {
+}
+
+Canvas::~Canvas() {
+}
+
+// static
+void Canvas::SizeStringInt(const std::wstring& text,
+ const gfx::Font& font,
+ int *width, int *height, int flags) {
+ *width = font.GetStringWidth(text);
+ *height = font.height();
+}
+
+void Canvas::DrawStringInt(const std::wstring& text, const gfx::Font& font,
+ const SkColor& color, int x, int y, int w, int h,
+ int flags) {
+ if (!IntersectsClipRectInt(x, y, w, h))
+ return;
+
+ CGContextRef context = beginPlatformPaint();
+ CGContextSaveGState(context);
+
+ NSColor* ns_color = [NSColor colorWithDeviceRed:SkColorGetR(color) / 255.0
+ green:SkColorGetG(color) / 255.0
+ blue:SkColorGetB(color) / 255.0
+ alpha:1.0];
+ NSMutableParagraphStyle *ns_style =
+ [[[NSParagraphStyle alloc] init] autorelease];
+ if (flags & TEXT_ALIGN_CENTER)
+ [ns_style setAlignment:NSCenterTextAlignment];
+ // TODO(awalker): Implement the rest of the Canvas text flags
+
+ NSDictionary* attributes =
+ [NSDictionary dictionaryWithObjectsAndKeys:
+ font.nativeFont(), NSFontAttributeName,
+ ns_color, NSForegroundColorAttributeName,
+ ns_style, NSParagraphStyleAttributeName,
+ nil];
+
+ NSAttributedString* ns_string =
+ [[[NSAttributedString alloc] initWithString:base::SysWideToNSString(text)
+ attributes:attributes] autorelease];
+ scoped_cftyperef<CTFramesetterRef> framesetter(
+ CTFramesetterCreateWithAttributedString(reinterpret_cast<CFAttributedStringRef>(ns_string)));
+
+ CGRect text_bounds = CGRectMake(x, y, w, h);
+ CGMutablePathRef path = CGPathCreateMutable();
+ CGPathAddRect(path, NULL, text_bounds);
+
+ scoped_cftyperef<CTFrameRef> frame(
+ CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, NULL));
+ CTFrameDraw(frame, context);
+ CGContextRestoreGState(context);
+ endPlatformPaint();
+}
+
+} // namespace gfx
diff --git a/gfx/canvas_paint.h b/gfx/canvas_paint.h
new file mode 100644
index 0000000..a11a693
--- /dev/null
+++ b/gfx/canvas_paint.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2009 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 GFX_CANVAS_PAINT_H_
+#define GFX_CANVAS_PAINT_H_
+
+#include "gfx/canvas.h"
+#include "skia/ext/canvas_paint.h"
+
+// Define a skia::CanvasPaint type that wraps our gfx::Canvas like the
+// skia::PlatformCanvasPaint wraps PlatformCanvas.
+
+namespace gfx {
+
+typedef skia::CanvasPaintT<Canvas> CanvasPaint;
+
+} // namespace gfx
+
+#endif // GFX_CANVAS_PAINT_H_
diff --git a/gfx/canvas_win.cc b/gfx/canvas_win.cc
new file mode 100644
index 0000000..be9756a
--- /dev/null
+++ b/gfx/canvas_win.cc
@@ -0,0 +1,286 @@
+// Copyright (c) 2010 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 "gfx/canvas.h"
+
+#include <limits>
+
+#include "base/i18n/rtl.h"
+#include "gfx/font.h"
+#include "gfx/rect.h"
+#include "third_party/skia/include/core/SkShader.h"
+
+namespace {
+
+// 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, const std::wstring& text,
+ RECT* text_bounds, int flags) {
+ std::wstring localized_text;
+ const wchar_t* string_ptr = text.c_str();
+ int string_size = static_cast<int>(text.length());
+ // Only adjust string directionality if both of the following are true:
+ // 1. The current locale is RTL.
+ // 2. The string itself has RTL directionality.
+ if (flags & DT_RTLREADING) {
+ if (base::i18n::AdjustStringForLocaleDirection(text, &localized_text)) {
+ string_ptr = localized_text.c_str();
+ string_size = static_cast<int>(localized_text.length());
+ }
+ }
+
+ DrawText(hdc, string_ptr, string_size, text_bounds, flags);
+}
+
+// Compute the windows flags necessary to implement the provided text Canvas
+// flags.
+int ComputeFormatFlags(int flags, const std::wstring& text) {
+ // Setting the text alignment explicitly in case it hasn't already been set.
+ // This will make sure that we don't align text to the left on RTL locales
+ // just because no alignment flag was passed to DrawStringInt().
+ if (!(flags & (gfx::Canvas::TEXT_ALIGN_CENTER |
+ gfx::Canvas::TEXT_ALIGN_RIGHT |
+ gfx::Canvas::TEXT_ALIGN_LEFT))) {
+ flags |= gfx::Canvas::DefaultCanvasTextAlignment();
+ }
+
+ // horizontal alignment
+ int f = 0;
+ if (flags & gfx::Canvas::TEXT_ALIGN_CENTER)
+ f |= DT_CENTER;
+ else if (flags & gfx::Canvas::TEXT_ALIGN_RIGHT)
+ f |= DT_RIGHT;
+ else
+ f |= DT_LEFT;
+
+ // vertical alignment
+ if (flags & gfx::Canvas::TEXT_VALIGN_TOP)
+ f |= DT_TOP;
+ else if (flags & gfx::Canvas::TEXT_VALIGN_BOTTOM)
+ f |= DT_BOTTOM;
+ else
+ f |= DT_VCENTER;
+
+ if (flags & gfx::Canvas::MULTI_LINE) {
+ f |= DT_WORDBREAK;
+ if (flags & gfx::Canvas::CHARACTER_BREAK)
+ f |= DT_EDITCONTROL; // Turns on character breaking (not documented)
+ else if (!(flags & gfx::Canvas::NO_ELLIPSIS))
+ f |= DT_WORD_ELLIPSIS;
+ } else {
+ f |= DT_SINGLELINE;
+ }
+
+ if (flags & gfx::Canvas::HIDE_PREFIX)
+ f |= DT_HIDEPREFIX;
+ else if ((flags & gfx::Canvas::SHOW_PREFIX) == 0)
+ f |= DT_NOPREFIX;
+
+ if (!(flags & gfx::Canvas::NO_ELLIPSIS))
+ f |= DT_END_ELLIPSIS;
+
+ // In order to make sure RTL/BiDi strings are rendered correctly, we must
+ // pass the flag DT_RTLREADING to DrawText (when the locale's language is
+ // a right-to-left language) so that Windows does the right thing.
+ //
+ // In addition to correctly displaying text containing both RTL and LTR
+ // elements (for example, a string containing a telephone number within a
+ // sentence in Hebrew, or a sentence in Hebrew that contains a word in
+ // English) this flag also makes sure that if there is not enough space to
+ // display the entire string, the ellipsis is displayed on the left hand side
+ // of the truncated string and not on the right hand side.
+ //
+ // We make a distinction between Chrome UI strings and text coming from a web
+ // page.
+ //
+ // For text coming from a web page we determine the alignment based on the
+ // first character with strong directionality. If the directionality of the
+ // first character with strong directionality in the text is LTR, the
+ // alignment is set to DT_LEFT, and the directionality should not be set as
+ // DT_RTLREADING.
+ //
+ // This heuristic doesn't work for Chrome UI strings since even in RTL
+ // locales, some of those might start with English text but we know they're
+ // localized so we always want them to be right aligned, and their
+ // directionality should be set as DT_RTLREADING.
+ //
+ // Caveat: If the string is purely LTR, don't set DTL_RTLREADING since when
+ // the flag is set, LRE-PDF don't have the desired effect of rendering
+ // multiline English-only text as LTR.
+ //
+ // Note that if the caller is explicitly requesting displaying the text
+ // using RTL directionality then we respect that and pass DT_RTLREADING to
+ // ::DrawText even if the locale is LTR.
+ if ((flags & gfx::Canvas::FORCE_RTL_DIRECTIONALITY) ||
+ (base::i18n::IsRTL() &&
+ (f & DT_RIGHT) && base::i18n::StringContainsStrongRTLChars(text))) {
+ f |= DT_RTLREADING;
+ }
+
+ return f;
+}
+
+} // anonymous namespace
+
+namespace gfx {
+
+Canvas::Canvas(int width, int height, bool is_opaque)
+ : skia::PlatformCanvas(width, height, is_opaque) {
+}
+
+Canvas::Canvas() : skia::PlatformCanvas() {
+}
+
+Canvas::~Canvas() {
+}
+
+// static
+void Canvas::SizeStringInt(const std::wstring& text,
+ const gfx::Font& font,
+ int *width, int *height, int flags) {
+ // Clamp the max amount of text we'll measure to 2K. When the string is
+ // actually drawn, it will be clipped to whatever size box is provided, and
+ // the time to do that doesn't depend on the length being clipped off.
+ const int kMaxStringLength = 2048;
+ std::wstring clamped_string(text.substr(0, kMaxStringLength));
+
+ if (*width == 0) {
+ // If multi-line + character break are on, the computed width will be one
+ // character wide (useless). Furthermore, if in this case the provided text
+ // contains very long "words" (substrings without a word-breaking point),
+ // DrawText() can run extremely slowly (e.g. several seconds). So in this
+ // case, we turn character breaking off to get a more accurate "desired"
+ // width and avoid the slowdown.
+ if (flags & (gfx::Canvas::MULTI_LINE | gfx::Canvas::CHARACTER_BREAK))
+ flags &= ~gfx::Canvas::CHARACTER_BREAK;
+
+ // Weird undocumented behavior: if the width is 0, DoDrawText() won't
+ // calculate a size at all. So set it to 1, which it will then change.
+ if (!text.empty())
+ *width = 1;
+ }
+ RECT r = { 0, 0, *width, *height };
+
+ HDC dc = GetDC(NULL);
+ HFONT old_font = static_cast<HFONT>(SelectObject(dc, font.hfont()));
+ DoDrawText(dc, clamped_string, &r,
+ ComputeFormatFlags(flags, clamped_string) | DT_CALCRECT);
+ SelectObject(dc, old_font);
+ ReleaseDC(NULL, dc);
+
+ *width = r.right;
+ *height = r.bottom;
+}
+
+void Canvas::DrawStringInt(const std::wstring& text, HFONT font,
+ const SkColor& color, int x, int y, int w, int h,
+ int flags) {
+ if (!IntersectsClipRectInt(x, y, w, h))
+ return;
+
+ RECT text_bounds = { x, y, x + w, y + h };
+ HDC dc = beginPlatformPaint();
+ SetBkMode(dc, TRANSPARENT);
+ HFONT old_font = (HFONT)SelectObject(dc, font);
+ COLORREF brush_color = RGB(SkColorGetR(color), SkColorGetG(color),
+ SkColorGetB(color));
+ SetTextColor(dc, brush_color);
+
+ int f = ComputeFormatFlags(flags, text);
+ DoDrawText(dc, text, &text_bounds, f);
+ endPlatformPaint();
+
+ // Restore the old font. This way we don't have to worry if the caller
+ // deletes the font and the DC lives longer.
+ SelectObject(dc, old_font);
+
+ // 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);
+}
+
+void Canvas::DrawStringInt(const std::wstring& text,
+ const gfx::Font& font,
+ const SkColor& color,
+ int x, int y, int w, int h, int flags) {
+ DrawStringInt(text, font.hfont(), color, x, y, w, h, flags);
+}
+
+// Checks each pixel immediately adjacent to the given pixel in the bitmap. If
+// any of them are not the halo color, returns true. This defines the halo of
+// pixels that will appear around the text. Note that we have to check each
+// pixel against both the halo color and transparent since DrawStringWithHalo
+// will modify the bitmap as it goes, and clears pixels shouldn't count as
+// changed.
+static bool pixelShouldGetHalo(const SkBitmap& bitmap, int x, int y,
+ SkColor halo_color) {
+ if (x > 0 &&
+ *bitmap.getAddr32(x - 1, y) != halo_color &&
+ *bitmap.getAddr32(x - 1, y) != 0)
+ return true; // Touched pixel to the left.
+ if (x < bitmap.width() - 1 &&
+ *bitmap.getAddr32(x + 1, y) != halo_color &&
+ *bitmap.getAddr32(x + 1, y) != 0)
+ return true; // Touched pixel to the right.
+ if (y > 0 &&
+ *bitmap.getAddr32(x, y - 1) != halo_color &&
+ *bitmap.getAddr32(x, y - 1) != 0)
+ return true; // Touched pixel above.
+ if (y < bitmap.height() - 1 &&
+ *bitmap.getAddr32(x, y + 1) != halo_color &&
+ *bitmap.getAddr32(x, y + 1) != 0)
+ return true; // Touched pixel below.
+ return false;
+}
+
+void Canvas::DrawStringWithHalo(const std::wstring& text,
+ const gfx::Font& font,
+ const SkColor& text_color,
+ const SkColor& halo_color_in,
+ int x, int y, int w, int h,
+ int flags) {
+ // Some callers will have semitransparent halo colors, which we don't handle
+ // (since the resulting image can have 1-bit transparency only).
+ SkColor halo_color = halo_color_in | 0xFF000000;
+
+ // Create a temporary buffer filled with the halo color. It must leave room
+ // for the 1-pixel border around the text.
+ Canvas text_canvas(w + 2, h + 2, true);
+ SkPaint bkgnd_paint;
+ bkgnd_paint.setColor(halo_color);
+ text_canvas.FillRectInt(0, 0, w + 2, h + 2, bkgnd_paint);
+
+ // Draw the text into the temporary buffer. This will have correct
+ // ClearType since the background color is the same as the halo color.
+ text_canvas.DrawStringInt(text, font, text_color, 1, 1, w, h, flags);
+
+ // Windows will have cleared the alpha channel for the pixels it drew. Make it
+ // opaque. We have to do this first since pixelShouldGetHalo will check for
+ // 0 to see if a pixel has been modified to transparent, and black text that
+ // Windows draw will look transparent to it!
+ text_canvas.getTopPlatformDevice().makeOpaque(0, 0, w + 2, h + 2);
+
+ uint32_t halo_premul = SkPreMultiplyColor(halo_color);
+ SkBitmap& text_bitmap = const_cast<SkBitmap&>(
+ text_canvas.getTopPlatformDevice().accessBitmap(true));
+ for (int cur_y = 0; cur_y < h + 2; cur_y++) {
+ uint32_t* text_row = text_bitmap.getAddr32(0, cur_y);
+ for (int cur_x = 0; cur_x < w + 2; cur_x++) {
+ if (text_row[cur_x] == halo_premul) {
+ // This pixel was not touched by the text routines. See if it borders
+ // a touched pixel in any of the 4 directions (not diagonally).
+ if (!pixelShouldGetHalo(text_bitmap, cur_x, cur_y, halo_premul))
+ text_row[cur_x] = 0; // Make transparent.
+ } else {
+ text_row[cur_x] |= 0xff << SK_A32_SHIFT; // Make opaque.
+ }
+ }
+ }
+
+ // Draw the halo bitmap with blur.
+ drawBitmap(text_bitmap, SkIntToScalar(x - 1), SkIntToScalar(y - 1));
+}
+
+} // namespace gfx
diff --git a/gfx/font.h b/gfx/font.h
new file mode 100644
index 0000000..7395a4d
--- /dev/null
+++ b/gfx/font.h
@@ -0,0 +1,287 @@
+// Copyright (c) 2006-2008 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 GFX_FONT_H_
+#define GFX_FONT_H_
+
+#include "build/build_config.h"
+
+#include <string>
+
+#if defined(OS_WIN)
+typedef struct HFONT__* HFONT;
+#elif !defined(OS_MACOSX)
+#include "third_party/skia/include/core/SkRefCnt.h"
+class SkPaint;
+class SkTypeface;
+#endif
+
+#if defined(OS_WIN)
+typedef struct HFONT__* NativeFont;
+#elif defined(OS_MACOSX)
+#ifdef __OBJC__
+@class NSFont;
+#else
+class NSFont;
+#endif
+typedef NSFont* NativeFont;
+#else
+typedef struct _PangoFontDescription PangoFontDescription;
+class SkTypeface;
+typedef SkTypeface* NativeFont;
+#endif
+
+#include "base/basictypes.h"
+#include "base/ref_counted.h"
+#include "base/scoped_ptr.h"
+
+namespace gfx {
+
+// Font provides a wrapper around an underlying font. Copy and assignment
+// operators are explicitly allowed, and cheap.
+class Font {
+ public:
+ // The following constants indicate the font style.
+ enum {
+ NORMAL = 0,
+ BOLD = 1,
+ ITALIC = 2,
+ UNDERLINED = 4,
+ };
+
+ // Creates a Font given font name (e.g. arial), font size (e.g. 12).
+ // Skia actually expects a family name and not a font name.
+ static Font CreateFont(const std::wstring& font_name, int font_size);
+
+ ~Font() { }
+
+ // Returns a new Font derived from the existing font.
+ // size_deta is the size to add to the current font. For example, a value
+ // of 5 results in a font 5 units bigger than this font.
+ Font DeriveFont(int size_delta) const {
+ return DeriveFont(size_delta, style());
+ }
+
+ // Returns a new Font derived from the existing font.
+ // size_delta is the size 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 UNDERLINED.
+ Font DeriveFont(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
+ // greater than just ascent + descent. Specifically, the Windows and Mac
+ // implementations include leading and the Linux one does not. This may
+ // need to be revisited in the future.
+ int height() const;
+
+ // Returns the baseline, or ascent, of the font.
+ int baseline() const;
+
+ // Returns the average character width for the font.
+ int ave_char_width() const;
+
+ // Returns the number of horizontal pixels needed to display the specified
+ // string.
+ int GetStringWidth(const std::wstring& text) 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 style of the font.
+ int style() const;
+
+ // Font Name.
+ // It is actually a font family name, because Skia expects a family name
+ // and not a font name.
+ const std::wstring& FontName() const;
+
+ // Font Size.
+ int FontSize();
+
+ NativeFont nativeFont() const;
+
+ // Creates a font with the default name and style.
+ Font();
+
+#if defined(OS_WIN)
+ // Creates a Font from the specified HFONT. The supplied HFONT is effectively
+ // copied.
+ static Font CreateFont(HFONT hfont);
+
+ // Returns the handle to the underlying HFONT. This is used by gfx::Canvas to
+ // draw text.
+ HFONT hfont() const { return font_ref_->hfont(); }
+
+ // Dialog units to pixels conversion.
+ // See http://support.microsoft.com/kb/145994 for details.
+ int horizontal_dlus_to_pixels(int dlus) {
+ return dlus * font_ref_->dlu_base_x() / 4;
+ }
+ int vertical_dlus_to_pixels(int dlus) {
+ return dlus * font_ref_->height() / 8;
+ }
+
+ // Callback that returns the minimum height that should be used for
+ // gfx::Fonts. Optional. If not specified, the minimum font size is 0.
+ typedef int (*GetMinimumFontSizeCallback)();
+ static GetMinimumFontSizeCallback get_minimum_font_size_callback;
+
+ // Callback that adjusts a LOGFONT to meet suitability requirements of the
+ // embedding application. Optional. If not specified, no adjustments are
+ // performed other than clamping to a minimum font height if
+ // |get_minimum_font_size_callback| is specified.
+ typedef void (*AdjustFontCallback)(LOGFONT* lf);
+ static AdjustFontCallback adjust_font_callback;
+
+#elif !defined(OS_MACOSX)
+ static Font CreateFont(PangoFontDescription* desc);
+ // We need a copy constructor and assignment operator to deal with
+ // the Skia reference counting.
+ Font(const Font& other);
+ Font& operator=(const Font& other);
+ // Setup a Skia context to use the current typeface
+ void PaintSetup(SkPaint* paint) const;
+
+ // Converts |gfx_font| to a new pango font. Free the returned font with
+ // pango_font_description_free().
+ static PangoFontDescription* PangoFontFromGfxFont(const gfx::Font& gfx_font);
+
+ // Position as an offset from the height of the drawn text, used to draw
+ // an underline. This is a negative number, so the underline would be
+ // drawn at y + height + underline_position;
+ double underline_position() const;
+ // The thickness to draw the underline.
+ double underline_thickness() const;
+#endif
+
+ private:
+
+#if defined(OS_WIN)
+ // Chrome text drawing bottoms out in the Windows GDI functions that take an
+ // HFONT (an opaque handle into Windows). To avoid lots of GDI object
+ // allocation and destruction, Font indirectly refers to the HFONT by way of
+ // an HFontRef. That is, every Font has an HFontRef, which has an HFONT.
+ //
+ // HFontRef is reference counted. Upon deletion, it deletes the HFONT.
+ // By making HFontRef maintain the reference to the HFONT, multiple
+ // HFontRefs can share the same HFONT, and Font can provide value semantics.
+ class HFontRef : public base::RefCounted<HFontRef> {
+ public:
+ // This constructor takes control of the HFONT, and will delete it when
+ // the HFontRef is deleted.
+ HFontRef(HFONT hfont,
+ int height,
+ int baseline,
+ int ave_char_width,
+ int style,
+ int dlu_base_x);
+
+ // Accessors
+ HFONT hfont() const { return hfont_; }
+ int height() const { return height_; }
+ int baseline() const { return baseline_; }
+ int ave_char_width() const { return ave_char_width_; }
+ int style() const { return style_; }
+ int dlu_base_x() const { return dlu_base_x_; }
+ const std::wstring& font_name() const { return font_name_; }
+
+ private:
+ friend class base::RefCounted<HFontRef>;
+
+ ~HFontRef();
+
+ const HFONT hfont_;
+ const int height_;
+ const int baseline_;
+ const int ave_char_width_;
+ const int style_;
+ // Constants used in converting dialog units to pixels.
+ const int dlu_base_x_;
+ std::wstring font_name_;
+
+ DISALLOW_COPY_AND_ASSIGN(HFontRef);
+ };
+
+ // Returns the base font ref. This should ONLY be invoked on the
+ // UI thread.
+ static HFontRef* GetBaseFontRef();
+
+ // Creates and returns a new HFONTRef from the specified HFONT.
+ static HFontRef* CreateHFontRef(HFONT font);
+
+ explicit Font(HFontRef* font_ref) : font_ref_(font_ref) { }
+
+ // Reference to the base font all fonts are derived from.
+ static HFontRef* base_font_ref_;
+
+ // Indirect reference to the HFontRef, which references the underlying HFONT.
+ scoped_refptr<HFontRef> font_ref_;
+#elif !defined(OS_MACOSX)
+ explicit Font(SkTypeface* typeface, const std::wstring& name,
+ int size, int style);
+ // Calculate and cache the font metrics.
+ void calculateMetrics();
+ // Make |this| a copy of |other|.
+ void CopyFont(const Font& other);
+
+ // The default font, used for the default constructor.
+ static Font* default_font_;
+
+ // Return the scale factor for fonts that account for DPI. We clamp the
+ // max DPI to prevent large fonts from overflowing UI elements.
+ static float GetPangoScaleFactor();
+
+ // The average width of a character, initialized and cached if needed.
+ double avg_width() const;
+
+ // Potentially slow call to get pango metrics (avg width, underline info).
+ void InitPangoMetrics();
+
+ // These two both point to the same SkTypeface. We use the SkAutoUnref to
+ // handle the reference counting, but without @typeface_ we would have to
+ // cast the SkRefCnt from @typeface_helper_ every time.
+ scoped_ptr<SkAutoUnref> typeface_helper_;
+ SkTypeface *typeface_;
+
+ // Additional information about the face
+ // Skia actually expects a family name and not a font name.
+ std::wstring font_family_;
+ int font_size_;
+ int style_;
+
+ // Cached metrics, generated at construction
+ int height_;
+ int ascent_;
+
+ // The pango metrics are much more expensive so we wait until we need them
+ // to compute them.
+ bool pango_metrics_inited_;
+ double avg_width_;
+ double underline_position_;
+ double underline_thickness_;
+#else // OS_MACOSX
+ explicit Font(const std::wstring& font_name, int font_size, int style);
+
+ // Calculate and cache the font metrics.
+ void calculateMetrics();
+
+ std::wstring font_name_;
+ int font_size_;
+ int style_;
+
+ // Cached metrics, generated at construction
+ int height_;
+ int ascent_;
+ int avg_width_;
+#endif
+
+};
+
+} // namespace gfx
+
+#endif // GFX_FONT_H_
diff --git a/gfx/font_gtk.cc b/gfx/font_gtk.cc
new file mode 100644
index 0000000..7dee3a1
--- /dev/null
+++ b/gfx/font_gtk.cc
@@ -0,0 +1,156 @@
+// Copyright (c) 2006-2008 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 "gfx/font.h"
+
+#include <algorithm>
+#include <fontconfig/fontconfig.h>
+#include <gtk/gtk.h>
+
+#include "base/logging.h"
+#include "base/string_piece.h"
+#include "base/utf_string_conversions.h"
+
+namespace gfx {
+
+Font* Font::default_font_ = NULL;
+
+// Find the best match font for |family_name| in the same way as Skia
+// to make sure CreateFont() successfully creates a default font. In
+// Skia, it only checks the best match font. If it failed to find
+// one, SkTypeface will be NULL for that font family. It eventually
+// causes a segfault. For example, family_name = "Sans" and system
+// may have various fonts. The first font family in FcPattern will be
+// "DejaVu Sans" but a font family returned by FcFontMatch will be "VL
+// PGothic". In this case, SkTypeface for "Sans" returns NULL even if
+// the system has a font for "Sans" font family. See FontMatch() in
+// skia/ports/SkFontHost_fontconfig.cpp for more detail.
+static std::wstring FindBestMatchFontFamilyName(const char* family_name) {
+ FcPattern* pattern = FcPatternCreate();
+ FcValue fcvalue;
+ fcvalue.type = FcTypeString;
+ char* family_name_copy = strdup(family_name);
+ fcvalue.u.s = reinterpret_cast<FcChar8*>(family_name_copy);
+ FcPatternAdd(pattern, FC_FAMILY, fcvalue, 0);
+ FcConfigSubstitute(0, pattern, FcMatchPattern);
+ FcDefaultSubstitute(pattern);
+ FcResult result;
+ FcPattern* match = FcFontMatch(0, pattern, &result);
+ DCHECK(match) << "Could not find font: " << family_name;
+ FcChar8* match_family;
+ FcPatternGetString(match, FC_FAMILY, 0, &match_family);
+
+ std::wstring font_family = UTF8ToWide(
+ reinterpret_cast<char*>(match_family));
+ FcPatternDestroy(match);
+ FcPatternDestroy(pattern);
+ free(family_name_copy);
+ return font_family;
+}
+
+// Pango scales font sizes. This returns the scale factor. See
+// pango_cairo_context_set_resolution for details.
+// NOTE: this isn't entirely accurate, in that Pango also consults the
+// FC_PIXEL_SIZE first (see get_font_size in pangocairo-fcfont), but this
+// seems to give us the same sizes as used by Pango for all our fonts in both
+// English and Thai.
+float Font::GetPangoScaleFactor() {
+ static float scale_factor = 0;
+ static bool determined_scale = false;
+ if (!determined_scale) {
+ PangoContext* context = gdk_pango_context_get();
+ scale_factor = pango_cairo_context_get_resolution(context);
+ // Until we switch to vector graphics, force the max DPI to 96.0.
+ scale_factor = std::min(scale_factor, 96.f);
+ g_object_unref(context);
+ if (scale_factor <= 0)
+ scale_factor = 1;
+ else
+ scale_factor /= 72.0;
+ determined_scale = true;
+ }
+ return scale_factor;
+}
+
+// static
+Font Font::CreateFont(PangoFontDescription* desc) {
+ gint size = pango_font_description_get_size(desc);
+ const char* family_name = pango_font_description_get_family(desc);
+
+ // Find best match font for |family_name| to make sure we can get
+ // a SkTypeface for the default font.
+ // TODO(agl): remove this.
+ std::wstring font_family = FindBestMatchFontFamilyName(family_name);
+
+ Font font = CreateFont(font_family, size / PANGO_SCALE);
+ int style = 0;
+ if (pango_font_description_get_weight(desc) == PANGO_WEIGHT_BOLD) {
+ // TODO(davemoore) What should we do about other weights? We currently
+ // only support BOLD.
+ style |= BOLD;
+ }
+ if (pango_font_description_get_style(desc) == PANGO_STYLE_ITALIC) {
+ // TODO(davemoore) What about PANGO_STYLE_OBLIQUE?
+ style |= ITALIC;
+ }
+ if (style != 0) {
+ font = font.DeriveFont(0, style);
+ }
+ return Font(font);
+}
+
+// Get the default gtk system font (name and size).
+Font::Font() {
+ if (default_font_ == NULL) {
+ 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.";
+
+ PangoFontDescription* desc =
+ pango_font_description_from_string(font_name);
+ default_font_ = new Font(CreateFont(desc));
+ pango_font_description_free(desc);
+ g_free(font_name);
+
+ DCHECK(default_font_);
+ }
+
+ CopyFont(*default_font_);
+}
+
+// static
+PangoFontDescription* Font::PangoFontFromGfxFont(
+ const gfx::Font& gfx_font) {
+ gfx::Font font = gfx_font; // Copy so we can call non-const methods.
+ PangoFontDescription* pfd = pango_font_description_new();
+ pango_font_description_set_family(pfd, WideToUTF8(font.FontName()).c_str());
+ // Set the absolute size to avoid overflowing UI elements.
+ pango_font_description_set_absolute_size(pfd,
+ font.FontSize() * PANGO_SCALE * Font::GetPangoScaleFactor());
+
+ switch (font.style()) {
+ case gfx::Font::NORMAL:
+ // Nothing to do, should already be PANGO_STYLE_NORMAL.
+ break;
+ case gfx::Font::BOLD:
+ pango_font_description_set_weight(pfd, PANGO_WEIGHT_BOLD);
+ break;
+ case gfx::Font::ITALIC:
+ pango_font_description_set_style(pfd, PANGO_STYLE_ITALIC);
+ break;
+ case gfx::Font::UNDERLINED:
+ // TODO(deanm): How to do underlined? Where do we use it? Probably have
+ // to paint it ourselves, see pango_font_metrics_get_underline_position.
+ break;
+ }
+
+ return pfd;
+}
+
+} // namespace gfx
diff --git a/gfx/font_mac.mm b/gfx/font_mac.mm
new file mode 100644
index 0000000..fcc85833
--- /dev/null
+++ b/gfx/font_mac.mm
@@ -0,0 +1,93 @@
+// Copyright (c) 2009 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 "gfx/font.h"
+
+#include <Cocoa/Cocoa.h>
+
+#include "base/logging.h"
+#include "base/scoped_nsobject.h"
+#include "base/sys_string_conversions.h"
+
+namespace gfx {
+
+// static
+Font Font::CreateFont(const std::wstring& font_name, int font_size) {
+ return Font(font_name, font_size, NORMAL);
+}
+
+Font::Font(const std::wstring& font_name, int font_size, int style)
+ : font_name_(font_name),
+ font_size_(font_size),
+ style_(style) {
+ calculateMetrics();
+}
+
+Font::Font()
+ : font_size_([NSFont systemFontSize]),
+ style_(NORMAL) {
+ NSFont* system_font = [NSFont systemFontOfSize:font_size_];
+ font_name_ = base::SysNSStringToWide([system_font fontName]);
+ calculateMetrics();
+}
+
+void Font::calculateMetrics() {
+ NSFont* font = nativeFont();
+ scoped_nsobject<NSLayoutManager> layout_manager(
+ [[NSLayoutManager alloc] init]);
+ height_ = [layout_manager defaultLineHeightForFont:font];
+ ascent_ = [font ascender];
+ avg_width_ = [font boundingRectForGlyph:[font glyphWithName:@"x"]].size.width;
+}
+
+Font Font::DeriveFont(int size_delta, int style) const {
+ return Font(font_name_, font_size_ + size_delta, style);
+}
+
+int Font::height() const {
+ return height_;
+}
+
+int Font::baseline() const {
+ return ascent_;
+}
+
+int Font::ave_char_width() const {
+ return avg_width_;
+}
+
+int Font::GetStringWidth(const std::wstring& text) const {
+ NSFont* font = nativeFont();
+ NSString* ns_string = base::SysWideToNSString(text);
+ NSDictionary* attributes =
+ [NSDictionary dictionaryWithObject:font forKey:NSFontAttributeName];
+ NSSize string_size = [ns_string sizeWithAttributes:attributes];
+ return string_size.width;
+}
+
+int Font::GetExpectedTextWidth(int length) const {
+ return length * avg_width_;
+}
+
+int Font::style() const {
+ return style_;
+}
+
+const std::wstring& Font::FontName() const {
+ return font_name_;
+}
+
+int Font::FontSize() {
+ return font_size_;
+}
+
+NativeFont Font::nativeFont() const {
+ // TODO(pinkerton): apply |style_| to font. http://crbug.com/34667
+ // We could cache this, but then we'd have to conditionally change the
+ // dtor just for MacOS. Not sure if we want to/need to do that.
+ return [NSFont fontWithName:base::SysWideToNSString(font_name_)
+ size:font_size_];
+}
+
+} // namespace gfx
diff --git a/gfx/font_skia.cc b/gfx/font_skia.cc
new file mode 100644
index 0000000..fffc53e
--- /dev/null
+++ b/gfx/font_skia.cc
@@ -0,0 +1,255 @@
+// Copyright (c) 2006-2008 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 "gfx/font.h"
+
+#include <gdk/gdk.h>
+#include <map>
+#include <pango/pango.h>
+
+#include "base/logging.h"
+#include "base/string_piece.h"
+#include "base/sys_string_conversions.h"
+#include "gfx/canvas.h"
+#include "third_party/skia/include/core/SkTypeface.h"
+#include "third_party/skia/include/core/SkPaint.h"
+
+namespace {
+
+// The font family name which is used when a user's application font for
+// GNOME/KDE is a non-scalable one. The name should be listed in the
+// IsFallbackFontAllowed function in skia/ext/SkFontHost_fontconfig_direct.cpp.
+const char* kFallbackFontFamilyName = "sans";
+
+// Retrieves the pango metrics for a pango font description. Caches the metrics
+// and never frees them. The metrics objects are relatively small and
+// very expensive to look up.
+static PangoFontMetrics* GetPangoFontMetrics(PangoFontDescription* desc) {
+ static std::map<int, PangoFontMetrics*>* desc_to_metrics = NULL;
+ static PangoContext* context = NULL;
+
+ if (!context) {
+ context = gdk_pango_context_get_for_screen(gdk_screen_get_default());
+ pango_context_set_language(context, pango_language_get_default());
+ }
+
+ if (!desc_to_metrics) {
+ desc_to_metrics = new std::map<int, PangoFontMetrics*>();
+ }
+
+ int desc_hash = pango_font_description_hash(desc);
+ std::map<int, PangoFontMetrics*>::iterator i =
+ desc_to_metrics->find(desc_hash);
+
+ if (i == desc_to_metrics->end()) {
+ PangoFontMetrics* metrics = pango_context_get_metrics(context, desc, NULL);
+ (*desc_to_metrics)[desc_hash] = metrics;
+ return metrics;
+ } else {
+ return i->second;
+ }
+}
+
+} // namespace
+
+namespace gfx {
+
+Font::Font(const Font& other) {
+ CopyFont(other);
+}
+
+Font& Font::operator=(const Font& other) {
+ CopyFont(other);
+ return *this;
+}
+
+Font::Font(SkTypeface* tf, const std::wstring& font_family, int font_size,
+ int style)
+ : typeface_helper_(new SkAutoUnref(tf)),
+ typeface_(tf),
+ font_family_(font_family),
+ font_size_(font_size),
+ style_(style),
+ pango_metrics_inited_(false),
+ avg_width_(0.0),
+ underline_position_(0.0),
+ underline_thickness_(0.0) {
+ tf->ref();
+ calculateMetrics();
+}
+
+void Font::calculateMetrics() {
+ SkPaint paint;
+ SkPaint::FontMetrics metrics;
+ PaintSetup(&paint);
+ paint.getFontMetrics(&metrics);
+
+ ascent_ = SkScalarCeil(-metrics.fAscent);
+ height_ = ascent_ + SkScalarCeil(metrics.fDescent);
+
+}
+
+void Font::CopyFont(const Font& other) {
+ typeface_helper_.reset(new SkAutoUnref(other.typeface_));
+ typeface_ = other.typeface_;
+ typeface_->ref();
+ font_family_ = other.font_family_;
+ font_size_ = other.font_size_;
+ style_ = other.style_;
+ height_ = other.height_;
+ ascent_ = other.ascent_;
+ pango_metrics_inited_ = other.pango_metrics_inited_;
+ avg_width_ = other.avg_width_;
+ underline_position_ = other.underline_position_;
+ underline_thickness_ = other.underline_thickness_;
+}
+
+int Font::height() const {
+ return height_;
+}
+
+int Font::baseline() const {
+ return ascent_;
+}
+
+int Font::ave_char_width() const {
+ return SkScalarRound(avg_width());
+}
+
+Font Font::CreateFont(const std::wstring& font_family, int font_size) {
+ DCHECK_GT(font_size, 0);
+ std::wstring fallback;
+
+ SkTypeface* tf = SkTypeface::CreateFromName(
+ base::SysWideToUTF8(font_family).c_str(), SkTypeface::kNormal);
+ if (!tf) {
+ // A non-scalable font such as .pcf is specified. Falls back to a default
+ // scalable font.
+ tf = SkTypeface::CreateFromName(
+ kFallbackFontFamilyName, SkTypeface::kNormal);
+ CHECK(tf) << "Could not find any font: "
+ << base::SysWideToUTF8(font_family)
+ << ", " << kFallbackFontFamilyName;
+ fallback = base::SysUTF8ToWide(kFallbackFontFamilyName);
+ }
+ SkAutoUnref tf_helper(tf);
+
+ return Font(
+ tf, fallback.empty() ? font_family : fallback, font_size, NORMAL);
+}
+
+Font Font::DeriveFont(int size_delta, int style) const {
+ // If the delta is negative, if must not push the size below 1
+ if (size_delta < 0) {
+ DCHECK_LT(-size_delta, font_size_);
+ }
+
+ if (style == style_) {
+ // Fast path, we just use the same typeface at a different size
+ return Font(typeface_, font_family_, font_size_ + size_delta, style_);
+ }
+
+ // If the style has changed we may need to load a new face
+ int skstyle = SkTypeface::kNormal;
+ if (BOLD & style)
+ skstyle |= SkTypeface::kBold;
+ if (ITALIC & style)
+ skstyle |= SkTypeface::kItalic;
+
+ SkTypeface* tf = SkTypeface::CreateFromName(
+ base::SysWideToUTF8(font_family_).c_str(),
+ static_cast<SkTypeface::Style>(skstyle));
+ SkAutoUnref tf_helper(tf);
+
+ return Font(tf, font_family_, font_size_ + size_delta, style);
+}
+
+void Font::PaintSetup(SkPaint* paint) const {
+ paint->setAntiAlias(false);
+ paint->setSubpixelText(false);
+ paint->setTextSize(SkFloatToScalar(font_size_ * Font::GetPangoScaleFactor()));
+ paint->setTypeface(typeface_);
+ paint->setFakeBoldText((BOLD & style_) && !typeface_->isBold());
+ paint->setTextSkewX((ITALIC & style_) && !typeface_->isItalic() ?
+ -SK_Scalar1/4 : 0);
+}
+
+int Font::GetStringWidth(const std::wstring& text) const {
+ int width = 0, height = 0;
+
+ Canvas::SizeStringInt(text, *this, &width, &height, 0);
+ return width;
+}
+
+void Font::InitPangoMetrics() {
+ if (!pango_metrics_inited_) {
+ pango_metrics_inited_ = true;
+ PangoFontDescription* pango_desc = PangoFontFromGfxFont(*this);
+ PangoFontMetrics* pango_metrics = GetPangoFontMetrics(pango_desc);
+
+ underline_position_ =
+ pango_font_metrics_get_underline_position(pango_metrics);
+ underline_position_ /= PANGO_SCALE;
+
+ // todo(davemoore) Come up with a better solution.
+ // This is a hack, but without doing this the underlines
+ // we get end up fuzzy. So we align to the midpoint of a pixel.
+ underline_position_ /= 2;
+
+ underline_thickness_ =
+ pango_font_metrics_get_underline_thickness(pango_metrics);
+ underline_thickness_ /= PANGO_SCALE;
+
+ // First get the pango based width
+ double pango_width =
+ pango_font_metrics_get_approximate_char_width(pango_metrics);
+ pango_width /= PANGO_SCALE;
+
+ // Yes, this is how Microsoft recommends calculating the dialog unit
+ // conversions.
+ int text_width = GetStringWidth(
+ L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
+ double dialog_units = (text_width / 26 + 1) / 2;
+ avg_width_ = std::min(pango_width, dialog_units);
+ pango_font_description_free(pango_desc);
+ }
+}
+
+double Font::avg_width() const {
+ const_cast<Font*>(this)->InitPangoMetrics();
+ return avg_width_;
+}
+
+double Font::underline_position() const {
+ const_cast<Font*>(this)->InitPangoMetrics();
+ return underline_position_;
+}
+
+double Font::underline_thickness() const {
+ const_cast<Font*>(this)->InitPangoMetrics();
+ return underline_thickness_;
+}
+
+int Font::GetExpectedTextWidth(int length) const {
+ double char_width = const_cast<Font*>(this)->avg_width();
+ return round(static_cast<float>(length) * char_width);
+}
+
+int Font::style() const {
+ return style_;
+}
+
+const std::wstring& Font::FontName() const {
+ return font_family_;
+}
+
+int Font::FontSize() {
+ return font_size_;
+}
+
+NativeFont Font::nativeFont() const {
+ return typeface_;
+}
+
+} // namespace gfx
diff --git a/gfx/font_unittest.cc b/gfx/font_unittest.cc
new file mode 100644
index 0000000..d2310f8
--- /dev/null
+++ b/gfx/font_unittest.cc
@@ -0,0 +1,80 @@
+// Copyright (c) 2006-2008 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 "gfx/font.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+using gfx::Font;
+
+class FontTest : public testing::Test {
+};
+
+TEST_F(FontTest, LoadArial) {
+ Font cf(Font::CreateFont(L"Arial", 16));
+ ASSERT_TRUE(cf.nativeFont());
+ ASSERT_EQ(cf.style(), Font::NORMAL);
+ ASSERT_EQ(cf.FontSize(), 16);
+ ASSERT_EQ(cf.FontName(), L"Arial");
+}
+
+TEST_F(FontTest, LoadArialBold) {
+ Font cf(Font::CreateFont(L"Arial", 16));
+ Font bold(cf.DeriveFont(0, Font::BOLD));
+ ASSERT_TRUE(bold.nativeFont());
+ ASSERT_EQ(bold.style(), Font::BOLD);
+}
+
+TEST_F(FontTest, Ascent) {
+ Font cf(Font::CreateFont(L"Arial", 16));
+ ASSERT_GT(cf.baseline(), 2);
+ ASSERT_LE(cf.baseline(), 22);
+}
+
+TEST_F(FontTest, Height) {
+ Font cf(Font::CreateFont(L"Arial", 16));
+ ASSERT_GE(cf.height(), 16);
+ // TODO(akalin): Figure out why height is so large on Linux.
+ ASSERT_LE(cf.height(), 26);
+}
+
+TEST_F(FontTest, AvgWidths) {
+ Font cf(Font::CreateFont(L"Arial", 16));
+ ASSERT_EQ(cf.GetExpectedTextWidth(0), 0);
+ ASSERT_GT(cf.GetExpectedTextWidth(1), cf.GetExpectedTextWidth(0));
+ ASSERT_GT(cf.GetExpectedTextWidth(2), cf.GetExpectedTextWidth(1));
+ ASSERT_GT(cf.GetExpectedTextWidth(3), cf.GetExpectedTextWidth(2));
+}
+
+TEST_F(FontTest, Widths) {
+ Font cf(Font::CreateFont(L"Arial", 16));
+ ASSERT_EQ(cf.GetStringWidth(L""), 0);
+ ASSERT_GT(cf.GetStringWidth(L"a"), cf.GetStringWidth(L""));
+ ASSERT_GT(cf.GetStringWidth(L"ab"), cf.GetStringWidth(L"a"));
+ ASSERT_GT(cf.GetStringWidth(L"abc"), cf.GetStringWidth(L"ab"));
+}
+
+#if defined(OS_WIN)
+// TODO(beng): re-enable evening of 3/22.
+TEST_F(FontTest, DISABLED_DeriveFontResizesIfSizeTooSmall) {
+ // This creates font of height -8.
+ Font cf(Font::CreateFont(L"Arial", 6));
+ Font derived_font = cf.DeriveFont(-4);
+ LOGFONT font_info;
+ GetObject(derived_font.hfont(), sizeof(LOGFONT), &font_info);
+ EXPECT_EQ(-5, font_info.lfHeight);
+}
+
+TEST_F(FontTest, DISABLED_DeriveFontKeepsOriginalSizeIfHeightOk) {
+ // This creates font of height -8.
+ Font cf(Font::CreateFont(L"Arial", 6));
+ Font derived_font = cf.DeriveFont(-2);
+ LOGFONT font_info;
+ GetObject(derived_font.hfont(), sizeof(LOGFONT), &font_info);
+ EXPECT_EQ(-6, font_info.lfHeight);
+}
+#endif
+} // anonymous namespace
diff --git a/gfx/font_win.cc b/gfx/font_win.cc
new file mode 100644
index 0000000..b55057c
--- /dev/null
+++ b/gfx/font_win.cc
@@ -0,0 +1,224 @@
+// Copyright (c) 2006-2008 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 "gfx/font.h"
+
+#include <windows.h>
+#include <math.h>
+
+#include <algorithm>
+
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "base/win_util.h"
+
+namespace gfx {
+
+// static
+Font::HFontRef* Font::base_font_ref_;
+
+// static
+Font::AdjustFontCallback Font::adjust_font_callback = NULL;
+Font::GetMinimumFontSizeCallback Font::get_minimum_font_size_callback = NULL;
+
+// If the tmWeight field of a TEXTMETRIC structure has a value >= this, the
+// font is bold.
+static const int kTextMetricWeightBold = 700;
+
+// Returns either minimum font allowed for a current locale or
+// lf_height + size_delta value.
+static int AdjustFontSize(int lf_height, int size_delta) {
+ if (lf_height < 0) {
+ lf_height -= size_delta;
+ } else {
+ lf_height += size_delta;
+ }
+ int min_font_size = 0;
+ if (Font::get_minimum_font_size_callback)
+ min_font_size = Font::get_minimum_font_size_callback();
+ // Make sure lf_height is not smaller than allowed min font size for current
+ // locale.
+ if (abs(lf_height) < min_font_size) {
+ return lf_height < 0 ? -min_font_size : min_font_size;
+ } else {
+ return lf_height;
+ }
+}
+
+//
+// Font
+//
+
+Font::Font()
+ : font_ref_(GetBaseFontRef()) {
+}
+
+int Font::height() const {
+ return font_ref_->height();
+}
+
+int Font::baseline() const {
+ return font_ref_->baseline();
+}
+
+int Font::ave_char_width() const {
+ return font_ref_->ave_char_width();
+}
+
+int Font::GetExpectedTextWidth(int length) const {
+ return length * std::min(font_ref_->dlu_base_x(), ave_char_width());
+}
+
+int Font::style() const {
+ return font_ref_->style();
+}
+
+NativeFont Font::nativeFont() const {
+ return hfont();
+}
+
+// static
+Font Font::CreateFont(HFONT font) {
+ DCHECK(font);
+ LOGFONT font_info;
+ GetObject(font, sizeof(LOGFONT), &font_info);
+ return Font(CreateHFontRef(CreateFontIndirect(&font_info)));
+}
+
+Font Font::CreateFont(const std::wstring& font_name, int font_size) {
+ HDC hdc = GetDC(NULL);
+ long lf_height = -MulDiv(font_size, GetDeviceCaps(hdc, LOGPIXELSY), 72);
+ ReleaseDC(NULL, hdc);
+ HFONT hf = ::CreateFont(lf_height, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ font_name.c_str());
+ return Font::CreateFont(hf);
+}
+
+// static
+Font::HFontRef* Font::GetBaseFontRef() {
+ if (base_font_ref_ == NULL) {
+ NONCLIENTMETRICS metrics;
+ win_util::GetNonClientMetrics(&metrics);
+
+ if (adjust_font_callback)
+ adjust_font_callback(&metrics.lfMessageFont);
+ metrics.lfMessageFont.lfHeight =
+ AdjustFontSize(metrics.lfMessageFont.lfHeight, 0);
+ HFONT font = CreateFontIndirect(&metrics.lfMessageFont);
+ DLOG_ASSERT(font);
+ base_font_ref_ = Font::CreateHFontRef(font);
+ // base_font_ref_ is global, up the ref count so it's never deleted.
+ base_font_ref_->AddRef();
+ }
+ return base_font_ref_;
+}
+
+const std::wstring& Font::FontName() const {
+ return font_ref_->font_name();
+}
+
+int Font::FontSize() {
+ LOGFONT font_info;
+ GetObject(hfont(), sizeof(LOGFONT), &font_info);
+ long lf_height = font_info.lfHeight;
+ HDC hdc = GetDC(NULL);
+ int device_caps = GetDeviceCaps(hdc, LOGPIXELSY);
+ int font_size = 0;
+ if (device_caps != 0) {
+ float font_size_float = -static_cast<float>(lf_height)*72/device_caps;
+ font_size = static_cast<int>(::ceil(font_size_float - 0.5));
+ }
+ ReleaseDC(NULL, hdc);
+ return font_size;
+}
+
+Font::HFontRef::HFontRef(HFONT hfont,
+ int height,
+ int baseline,
+ int ave_char_width,
+ int style,
+ int dlu_base_x)
+ : hfont_(hfont),
+ height_(height),
+ baseline_(baseline),
+ ave_char_width_(ave_char_width),
+ style_(style),
+ dlu_base_x_(dlu_base_x) {
+ DLOG_ASSERT(hfont);
+
+ LOGFONT font_info;
+ GetObject(hfont_, sizeof(LOGFONT), &font_info);
+ font_name_ = std::wstring(font_info.lfFaceName);
+}
+
+Font::HFontRef::~HFontRef() {
+ DeleteObject(hfont_);
+}
+
+Font Font::DeriveFont(int size_delta, int style) const {
+ LOGFONT font_info;
+ GetObject(hfont(), sizeof(LOGFONT), &font_info);
+ font_info.lfHeight = AdjustFontSize(font_info.lfHeight, size_delta);
+ font_info.lfUnderline = ((style & UNDERLINED) == UNDERLINED);
+ font_info.lfItalic = ((style & ITALIC) == ITALIC);
+ font_info.lfWeight = (style & BOLD) ? FW_BOLD : FW_NORMAL;
+
+ HFONT hfont = CreateFontIndirect(&font_info);
+ return Font(CreateHFontRef(hfont));
+}
+
+int Font::GetStringWidth(const std::wstring& text) const {
+ int width = 0;
+ HDC dc = GetDC(NULL);
+ HFONT previous_font = static_cast<HFONT>(SelectObject(dc, hfont()));
+ SIZE size;
+ if (GetTextExtentPoint32(dc, text.c_str(), static_cast<int>(text.size()),
+ &size)) {
+ width = size.cx;
+ } else {
+ width = 0;
+ }
+ SelectObject(dc, previous_font);
+ ReleaseDC(NULL, dc);
+ return width;
+}
+
+Font::HFontRef* Font::CreateHFontRef(HFONT font) {
+ TEXTMETRIC font_metrics;
+ HDC screen_dc = GetDC(NULL);
+ HFONT previous_font = static_cast<HFONT>(SelectObject(screen_dc, font));
+ int last_map_mode = SetMapMode(screen_dc, MM_TEXT);
+ GetTextMetrics(screen_dc, &font_metrics);
+ // Yes, this is how Microsoft recommends calculating the dialog unit
+ // conversions.
+ SIZE ave_text_size;
+ GetTextExtentPoint32(screen_dc,
+ L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
+ 52, &ave_text_size);
+ const int dlu_base_x = (ave_text_size.cx / 26 + 1) / 2;
+ // To avoid the DC referencing font_handle_, select the previous font.
+ SelectObject(screen_dc, previous_font);
+ SetMapMode(screen_dc, last_map_mode);
+ ReleaseDC(NULL, screen_dc);
+
+ const int height = std::max(1, static_cast<int>(font_metrics.tmHeight));
+ const int baseline = std::max(1, static_cast<int>(font_metrics.tmAscent));
+ const int ave_char_width =
+ std::max(1, static_cast<int>(font_metrics.tmAveCharWidth));
+ int style = 0;
+ if (font_metrics.tmItalic) {
+ style |= Font::ITALIC;
+ }
+ if (font_metrics.tmUnderlined) {
+ style |= Font::UNDERLINED;
+ }
+ if (font_metrics.tmWeight >= kTextMetricWeightBold) {
+ style |= Font::BOLD;
+ }
+
+ return new HFontRef(font, height, baseline, ave_char_width, style,
+ dlu_base_x);
+}
+
+} // namespace gfx
diff --git a/gfx/gfx.gyp b/gfx/gfx.gyp
index 8a33525..4b7d612 100644
--- a/gfx/gfx.gyp
+++ b/gfx/gfx.gyp
@@ -20,6 +20,7 @@
'codec/jpeg_codec_unittest.cc',
'codec/png_codec_unittest.cc',
'color_utils_unittest.cc',
+ 'font_unittest.cc',
'insets_unittest.cc',
'rect_unittest.cc',
'run_all_unittests.cc',
@@ -61,6 +62,11 @@
'sources': [
'blit.cc',
'blit.h',
+ 'canvas.cc',
+ 'canvas.h',
+ 'canvas_linux.cc',
+ 'canvas_mac.mm',
+ 'canvas_win.cc',
'codec/jpeg_codec.cc',
'codec/jpeg_codec.h',
'codec/png_codec.cc',
@@ -68,6 +74,11 @@
'color_utils.cc',
'color_utils.h',
'favicon_size.h',
+ 'font.h',
+ 'font_gtk.cc',
+ 'font_mac.mm',
+ 'font_skia.cc',
+ 'font_win.cc',
'gfx_paths.cc',
'gfx_paths.h',
'insets.cc',
@@ -104,6 +115,11 @@
],
}],
['OS=="linux" or OS=="freebsd" or OS=="openbsd"', {
+ 'dependencies': [
+ # font_gtk.cc uses fontconfig.
+ # TODO(evanm): I think this is wrong; it should just use GTK.
+ '../build/linux/system.gyp:fontconfig',
+ ],
'sources': [
'gtk_native_view_id_manager.cc',
'gtk_native_view_id_manager.h',