summaryrefslogtreecommitdiffstats
path: root/ui
diff options
context:
space:
mode:
authorsail@chromium.org <sail@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-02-02 23:03:07 +0000
committersail@chromium.org <sail@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-02-02 23:03:07 +0000
commit267c03d478d9c83ef8c37999f3abb1cd822881ff (patch)
tree6286c650d0ec58cdee9c7482fda929252bd985b5 /ui
parent7c085463177741a3844215675577bba3de1be836 (diff)
downloadchromium_src-267c03d478d9c83ef8c37999f3abb1cd822881ff.zip
chromium_src-267c03d478d9c83ef8c37999f3abb1cd822881ff.tar.gz
chromium_src-267c03d478d9c83ef8c37999f3abb1cd822881ff.tar.bz2
Move src/gfx/ to src/ui/gfx
To reduce the size of this change I've left stub header files in src/gfx/. Once all includes have been updated I'll delete the stub files. BUG=71063 TEST=Still doing test builds. Review URL: http://codereview.chromium.org/6246027 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@73530 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ui')
-rw-r--r--ui/base/ui_base.gypi2
-rw-r--r--ui/gfx/DEPS5
-rw-r--r--ui/gfx/blit.cc202
-rw-r--r--ui/gfx/blit.h53
-rw-r--r--ui/gfx/blit_unittest.cc135
-rw-r--r--ui/gfx/brush.h24
-rw-r--r--ui/gfx/canvas.cc17
-rw-r--r--ui/gfx/canvas.h238
-rw-r--r--ui/gfx/canvas_direct2d.cc362
-rw-r--r--ui/gfx/canvas_direct2d.h112
-rw-r--r--ui/gfx/canvas_direct2d_unittest.cc350
-rw-r--r--ui/gfx/canvas_skia.cc391
-rw-r--r--ui/gfx/canvas_skia.h165
-rw-r--r--ui/gfx/canvas_skia_linux.cc387
-rw-r--r--ui/gfx/canvas_skia_mac.mm88
-rw-r--r--ui/gfx/canvas_skia_paint.h21
-rw-r--r--ui/gfx/canvas_skia_win.cc301
-rw-r--r--ui/gfx/codec/DEPS5
-rw-r--r--ui/gfx/codec/jpeg_codec.cc535
-rw-r--r--ui/gfx/codec/jpeg_codec.h68
-rw-r--r--ui/gfx/codec/jpeg_codec_unittest.cc151
-rw-r--r--ui/gfx/codec/png_codec.cc699
-rw-r--r--ui/gfx/codec/png_codec.h105
-rw-r--r--ui/gfx/codec/png_codec_unittest.cc297
-rw-r--r--ui/gfx/color_utils.cc300
-rw-r--r--ui/gfx/color_utils.h80
-rw-r--r--ui/gfx/color_utils_unittest.cc67
-rw-r--r--ui/gfx/empty.cc6
-rw-r--r--ui/gfx/favicon_size.h34
-rw-r--r--ui/gfx/font.cc85
-rw-r--r--ui/gfx/font.h113
-rw-r--r--ui/gfx/font_unittest.cc120
-rw-r--r--ui/gfx/gdi_util.cc79
-rw-r--r--ui/gfx/gdi_util.h38
-rw-r--r--ui/gfx/gfx.gyp231
-rw-r--r--ui/gfx/gfx_module.cc21
-rw-r--r--ui/gfx/gfx_module.h34
-rw-r--r--ui/gfx/gfx_paths.cc51
-rw-r--r--ui/gfx/gfx_paths.h28
-rw-r--r--ui/gfx/gfx_resources.grd40
-rw-r--r--ui/gfx/gtk_native_view_id_manager.cc223
-rw-r--r--ui/gfx/gtk_native_view_id_manager.h141
-rw-r--r--ui/gfx/gtk_preserve_window.cc200
-rw-r--r--ui/gfx/gtk_preserve_window.h64
-rw-r--r--ui/gfx/gtk_util.cc212
-rw-r--r--ui/gfx/gtk_util.h84
-rw-r--r--ui/gfx/icon_util.cc457
-rw-r--r--ui/gfx/icon_util.h194
-rw-r--r--ui/gfx/icon_util_unittest.cc256
-rw-r--r--ui/gfx/insets.cc16
-rw-r--r--ui/gfx/insets.h94
-rw-r--r--ui/gfx/insets_unittest.cc66
-rw-r--r--ui/gfx/native_theme_linux.cc941
-rw-r--r--ui/gfx/native_theme_linux.h238
-rw-r--r--ui/gfx/native_theme_win.cc874
-rw-r--r--ui/gfx/native_theme_win.h321
-rw-r--r--ui/gfx/native_theme_win_unittest.cc11
-rw-r--r--ui/gfx/native_widget_types.h176
-rw-r--r--ui/gfx/native_widget_types_gtk.cc15
-rw-r--r--ui/gfx/path.cc26
-rw-r--r--ui/gfx/path.h57
-rw-r--r--ui/gfx/path_gtk.cc55
-rw-r--r--ui/gfx/path_win.cc45
-rw-r--r--ui/gfx/platform_font.h79
-rw-r--r--ui/gfx/platform_font_gtk.cc450
-rw-r--r--ui/gfx/platform_font_gtk.h107
-rw-r--r--ui/gfx/platform_font_mac.h57
-rw-r--r--ui/gfx/platform_font_mac.mm143
-rw-r--r--ui/gfx/platform_font_win.cc269
-rw-r--r--ui/gfx/platform_font_win.h131
-rw-r--r--ui/gfx/point.cc56
-rw-r--r--ui/gfx/point.h101
-rw-r--r--ui/gfx/rect.cc256
-rw-r--r--ui/gfx/rect.h184
-rw-r--r--ui/gfx/rect_unittest.cc314
-rw-r--r--ui/gfx/run_all_unittests.cc9
-rw-r--r--ui/gfx/scoped_cg_context_state_mac.h30
-rw-r--r--ui/gfx/scoped_image.h147
-rw-r--r--ui/gfx/scoped_image_unittest.cc98
-rw-r--r--ui/gfx/scrollbar_size.cc23
-rw-r--r--ui/gfx/scrollbar_size.h18
-rw-r--r--ui/gfx/size.cc70
-rw-r--r--ui/gfx/size.h82
-rw-r--r--ui/gfx/skbitmap_operations.cc721
-rw-r--r--ui/gfx/skbitmap_operations.h102
-rw-r--r--ui/gfx/skbitmap_operations_unittest.cc517
-rw-r--r--ui/gfx/skia_util.cc61
-rw-r--r--ui/gfx/skia_util.h39
-rw-r--r--ui/gfx/skia_utils_gtk.cc32
-rw-r--r--ui/gfx/skia_utils_gtk.h23
-rw-r--r--ui/gfx/test_suite.cc48
-rw-r--r--ui/gfx/test_suite.h24
-rw-r--r--ui/gfx/win_util.cc63
-rw-r--r--ui/gfx/win_util.h20
94 files changed, 15079 insertions, 1 deletions
diff --git a/ui/base/ui_base.gypi b/ui/base/ui_base.gypi
index 3822d25..71443f6 100644
--- a/ui/base/ui_base.gypi
+++ b/ui/base/ui_base.gypi
@@ -46,7 +46,7 @@
'type': '<(library)',
'dependencies': [
'../base/base.gyp:base',
- '../gfx/gfx.gyp:gfx',
+ '../ui/gfx/gfx.gyp:gfx',
'../skia/skia.gyp:skia',
'../third_party/icu/icu.gyp:icui18n',
'../third_party/icu/icu.gyp:icuuc',
diff --git a/ui/gfx/DEPS b/ui/gfx/DEPS
new file mode 100644
index 0000000..548fe15
--- /dev/null
+++ b/ui/gfx/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+ "+base",
+ "+grit/gfx_resources.h",
+ "+skia",
+]
diff --git a/ui/gfx/blit.cc b/ui/gfx/blit.cc
new file mode 100644
index 0000000..8853339
--- /dev/null
+++ b/ui/gfx/blit.cc
@@ -0,0 +1,202 @@
+// 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/blit.h"
+
+#include "base/logging.h"
+#include "build/build_config.h"
+#include "gfx/point.h"
+#include "gfx/rect.h"
+#include "skia/ext/platform_canvas.h"
+#include "skia/ext/platform_device.h"
+
+#if defined(OS_POSIX) && !defined(OS_MACOSX)
+#include <cairo/cairo.h>
+#endif
+
+#if defined(OS_MACOSX)
+#include "base/mac/scoped_cftyperef.h"
+#endif
+
+namespace gfx {
+
+namespace {
+
+// Returns true if the given canvas has any part of itself clipped out or
+// any non-identity tranform.
+bool HasClipOrTransform(const skia::PlatformCanvas& canvas) {
+ if (!canvas.getTotalMatrix().isIdentity())
+ return true;
+
+ const SkRegion& clip_region = canvas.getTotalClip();
+ if (clip_region.isEmpty() || clip_region.isComplex())
+ return true;
+
+ // Now we know the clip is a regular rectangle, make sure it covers the
+ // entire canvas.
+ const SkBitmap& bitmap = canvas.getTopPlatformDevice().accessBitmap(false);
+ const SkIRect& clip_bounds = clip_region.getBounds();
+ if (clip_bounds.fLeft != 0 || clip_bounds.fTop != 0 ||
+ clip_bounds.fRight != bitmap.width() ||
+ clip_bounds.fBottom != bitmap.height())
+ return true;
+
+ return false;
+}
+
+} // namespace
+
+void BlitContextToContext(NativeDrawingContext dst_context,
+ const Rect& dst_rect,
+ NativeDrawingContext src_context,
+ const Point& src_origin) {
+#if defined(OS_WIN)
+ BitBlt(dst_context, dst_rect.x(), dst_rect.y(),
+ dst_rect.width(), dst_rect.height(),
+ src_context, src_origin.x(), src_origin.y(), SRCCOPY);
+#elif defined(OS_MACOSX)
+ // Only translations and/or vertical flips in the source context are
+ // supported; more complex source context transforms will be ignored.
+
+ // If there is a translation on the source context, we need to account for
+ // it ourselves since CGBitmapContextCreateImage will bypass it.
+ Rect src_rect(src_origin, dst_rect.size());
+ CGAffineTransform transform = CGContextGetCTM(src_context);
+ bool flipped = fabs(transform.d + 1) < 0.0001;
+ CGFloat delta_y = flipped ? CGBitmapContextGetHeight(src_context) -
+ transform.ty
+ : transform.ty;
+ src_rect.Offset(transform.tx, delta_y);
+
+ base::mac::ScopedCFTypeRef<CGImageRef>
+ src_image(CGBitmapContextCreateImage(src_context));
+ base::mac::ScopedCFTypeRef<CGImageRef> src_sub_image(
+ CGImageCreateWithImageInRect(src_image, src_rect.ToCGRect()));
+ CGContextDrawImage(dst_context, dst_rect.ToCGRect(), src_sub_image);
+#else // Linux, BSD, others
+ // Only translations in the source context are supported; more complex
+ // source context transforms will be ignored.
+ cairo_save(dst_context);
+ double surface_x = src_origin.x();
+ double surface_y = src_origin.y();
+ cairo_user_to_device(src_context, &surface_x, &surface_y);
+ cairo_set_source_surface(dst_context, cairo_get_target(src_context),
+ dst_rect.x()-surface_x, dst_rect.y()-surface_y);
+ cairo_rectangle(dst_context, dst_rect.x(), dst_rect.y(),
+ dst_rect.width(), dst_rect.height());
+ cairo_clip(dst_context);
+ cairo_paint(dst_context);
+ cairo_restore(dst_context);
+#endif
+}
+
+static NativeDrawingContext GetContextFromCanvas(
+ skia::PlatformCanvas *canvas) {
+ skia::PlatformDevice& device = canvas->getTopPlatformDevice();
+#if defined(OS_WIN)
+ return device.getBitmapDC();
+#elif defined(OS_MACOSX)
+ return device.GetBitmapContext();
+#else // Linux, BSD, others
+ return device.beginPlatformPaint();
+#endif
+}
+
+void BlitContextToCanvas(skia::PlatformCanvas *dst_canvas,
+ const Rect& dst_rect,
+ NativeDrawingContext src_context,
+ const Point& src_origin) {
+ BlitContextToContext(GetContextFromCanvas(dst_canvas), dst_rect,
+ src_context, src_origin);
+}
+
+void BlitCanvasToContext(NativeDrawingContext dst_context,
+ const Rect& dst_rect,
+ skia::PlatformCanvas *src_canvas,
+ const Point& src_origin) {
+ BlitContextToContext(dst_context, dst_rect,
+ GetContextFromCanvas(src_canvas), src_origin);
+}
+
+void BlitCanvasToCanvas(skia::PlatformCanvas *dst_canvas,
+ const Rect& dst_rect,
+ skia::PlatformCanvas *src_canvas,
+ const Point& src_origin) {
+ BlitContextToContext(GetContextFromCanvas(dst_canvas), dst_rect,
+ GetContextFromCanvas(src_canvas), src_origin);
+}
+
+#if defined(OS_WIN)
+
+void ScrollCanvas(skia::PlatformCanvas* canvas,
+ const gfx::Rect& clip,
+ const gfx::Point& amount) {
+ DCHECK(!HasClipOrTransform(*canvas)); // Don't support special stuff.
+ HDC hdc = canvas->beginPlatformPaint();
+
+ RECT damaged_rect;
+ RECT r = clip.ToRECT();
+ ScrollDC(hdc, amount.x(), amount.y(), NULL, &r, NULL, &damaged_rect);
+
+ canvas->endPlatformPaint();
+}
+
+#elif defined(OS_POSIX)
+// Cairo has no nice scroll function so we do our own. On Mac it's possible to
+// use platform scroll code, but it's complex so we just use the same path
+// here. Either way it will be software-only, so it shouldn't matter much.
+
+void ScrollCanvas(skia::PlatformCanvas* canvas,
+ const gfx::Rect& in_clip,
+ const gfx::Point& amount) {
+ DCHECK(!HasClipOrTransform(*canvas)); // Don't support special stuff.
+ SkBitmap& bitmap = const_cast<SkBitmap&>(
+ canvas->getTopPlatformDevice().accessBitmap(true));
+ SkAutoLockPixels lock(bitmap);
+
+ // We expect all coords to be inside the canvas, so clip here.
+ gfx::Rect clip = in_clip.Intersect(
+ gfx::Rect(0, 0, bitmap.width(), bitmap.height()));
+
+ // Compute the set of pixels we'll actually end up painting.
+ gfx::Rect dest_rect = clip;
+ dest_rect.Offset(amount);
+ dest_rect = dest_rect.Intersect(clip);
+ if (dest_rect.size() == gfx::Size())
+ return; // Nothing to do.
+
+ // Compute the source pixels that will map to the dest_rect
+ gfx::Rect src_rect = dest_rect;
+ src_rect.Offset(-amount.x(), -amount.y());
+
+ size_t row_bytes = dest_rect.width() * 4;
+ if (amount.y() > 0) {
+ // Data is moving down, copy from the bottom up.
+ for (int y = dest_rect.height() - 1; y >= 0; y--) {
+ memcpy(bitmap.getAddr32(dest_rect.x(), dest_rect.y() + y),
+ bitmap.getAddr32(src_rect.x(), src_rect.y() + y),
+ row_bytes);
+ }
+ } else if (amount.y() < 0) {
+ // Data is moving up, copy from the top down.
+ for (int y = 0; y < dest_rect.height(); y++) {
+ memcpy(bitmap.getAddr32(dest_rect.x(), dest_rect.y() + y),
+ bitmap.getAddr32(src_rect.x(), src_rect.y() + y),
+ row_bytes);
+ }
+ } else if (amount.x() != 0) {
+ // Horizontal-only scroll. We can do it in either top-to-bottom or bottom-
+ // to-top, but have to be careful about the order for copying each row.
+ // Fortunately, memmove already handles this for us.
+ for (int y = 0; y < dest_rect.height(); y++) {
+ memmove(bitmap.getAddr32(dest_rect.x(), dest_rect.y() + y),
+ bitmap.getAddr32(src_rect.x(), src_rect.y() + y),
+ row_bytes);
+ }
+ }
+}
+
+#endif
+
+} // namespace gfx
diff --git a/ui/gfx/blit.h b/ui/gfx/blit.h
new file mode 100644
index 0000000..d5b1b5e
--- /dev/null
+++ b/ui/gfx/blit.h
@@ -0,0 +1,53 @@
+// 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 UI_GFX_BLIT_H_
+#define UI_GFX_BLIT_H_
+#pragma once
+
+#include "gfx/native_widget_types.h"
+
+namespace skia {
+class PlatformCanvas;
+} // namespace skia
+
+namespace gfx {
+
+class Point;
+class Rect;
+
+// Blits a rectangle from the source context into the destination context.
+void BlitContextToContext(NativeDrawingContext dst_context,
+ const Rect& dst_rect,
+ NativeDrawingContext src_context,
+ const Point& src_origin);
+
+// Blits a rectangle from the source context into the destination canvas.
+void BlitContextToCanvas(skia::PlatformCanvas *dst_canvas,
+ const Rect& dst_rect,
+ NativeDrawingContext src_context,
+ const Point& src_origin);
+
+// Blits a rectangle from the source canvas into the destination context.
+void BlitCanvasToContext(NativeDrawingContext dst_context,
+ const Rect& dst_rect,
+ skia::PlatformCanvas *src_canvas,
+ const Point& src_origin);
+
+// Blits a rectangle from the source canvas into the destination canvas.
+void BlitCanvasToCanvas(skia::PlatformCanvas *dst_canvas,
+ const Rect& dst_rect,
+ skia::PlatformCanvas *src_canvas,
+ const Point& src_origin);
+
+// Scrolls the given subset of the given canvas by the given amount.
+// The canvas should not have a clip or a transform applied, since platforms
+// may implement those operations differently.
+void ScrollCanvas(skia::PlatformCanvas* canvas,
+ const Rect& clip,
+ const Point& amount);
+
+} // namespace gfx
+
+#endif // UI_GFX_BLIT_H_
diff --git a/ui/gfx/blit_unittest.cc b/ui/gfx/blit_unittest.cc
new file mode 100644
index 0000000..cea3296
--- /dev/null
+++ b/ui/gfx/blit_unittest.cc
@@ -0,0 +1,135 @@
+// 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 "base/basictypes.h"
+#include "gfx/blit.h"
+#include "gfx/point.h"
+#include "gfx/rect.h"
+#include "skia/ext/platform_canvas.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+// Fills the given canvas with the values by duplicating the values into each
+// color channel for the corresponding pixel.
+//
+// Example values = {{0x0, 0x01}, {0x12, 0xFF}} would give a canvas with:
+// 0x00000000 0x01010101
+// 0x12121212 0xFFFFFFFF
+template<int w, int h>
+void SetToCanvas(skia::PlatformCanvas* canvas, uint8 values[h][w]) {
+ SkBitmap& bitmap = const_cast<SkBitmap&>(
+ canvas->getTopPlatformDevice().accessBitmap(true));
+ SkAutoLockPixels lock(bitmap);
+ ASSERT_EQ(w, bitmap.width());
+ ASSERT_EQ(h, bitmap.height());
+
+ for (int y = 0; y < h; y++) {
+ for (int x = 0; x < w; x++) {
+ uint8 value = values[y][x];
+ *bitmap.getAddr32(x, y) =
+ (value << 24) | (value << 16) | (value << 8) | value;
+ }
+ }
+}
+
+// Checks each pixel in the given canvas and see if it is made up of the given
+// values, where each value has been duplicated into each channel of the given
+// bitmap (see SetToCanvas above).
+template<int w, int h>
+void VerifyCanvasValues(skia::PlatformCanvas* canvas, uint8 values[h][w]) {
+ SkBitmap& bitmap = const_cast<SkBitmap&>(
+ canvas->getTopPlatformDevice().accessBitmap(true));
+ SkAutoLockPixels lock(bitmap);
+ ASSERT_EQ(w, bitmap.width());
+ ASSERT_EQ(h, bitmap.height());
+
+ for (int y = 0; y < h; y++) {
+ for (int x = 0; x < w; x++) {
+ uint8 value = values[y][x];
+ uint32 expected =
+ (value << 24) | (value << 16) | (value << 8) | value;
+ ASSERT_EQ(expected, *bitmap.getAddr32(x, y));
+ }
+ }
+}
+
+} // namespace
+
+TEST(Blit, ScrollCanvas) {
+ static const int kCanvasWidth = 5;
+ static const int kCanvasHeight = 5;
+ skia::PlatformCanvas canvas(kCanvasWidth, kCanvasHeight, true);
+ uint8 initial_values[kCanvasHeight][kCanvasWidth] = {
+ { 0x00, 0x01, 0x02, 0x03, 0x04 },
+ { 0x10, 0x11, 0x12, 0x13, 0x14 },
+ { 0x20, 0x21, 0x22, 0x23, 0x24 },
+ { 0x30, 0x31, 0x32, 0x33, 0x34 },
+ { 0x40, 0x41, 0x42, 0x43, 0x44 }};
+ SetToCanvas<5, 5>(&canvas, initial_values);
+
+ // Sanity check on input.
+ VerifyCanvasValues<5, 5>(&canvas, initial_values);
+
+ // Scroll none and make sure it's a NOP.
+ gfx::ScrollCanvas(&canvas,
+ gfx::Rect(0, 0, kCanvasWidth, kCanvasHeight),
+ gfx::Point(0, 0));
+ VerifyCanvasValues<5, 5>(&canvas, initial_values);
+
+ // Scroll the center 3 pixels up one.
+ gfx::Rect center_three(1, 1, 3, 3);
+ gfx::ScrollCanvas(&canvas, center_three, gfx::Point(0, -1));
+ uint8 scroll_up_expected[kCanvasHeight][kCanvasWidth] = {
+ { 0x00, 0x01, 0x02, 0x03, 0x04 },
+ { 0x10, 0x21, 0x22, 0x23, 0x14 },
+ { 0x20, 0x31, 0x32, 0x33, 0x24 },
+ { 0x30, 0x31, 0x32, 0x33, 0x34 },
+ { 0x40, 0x41, 0x42, 0x43, 0x44 }};
+ VerifyCanvasValues<5, 5>(&canvas, scroll_up_expected);
+
+ // Reset and scroll the center 3 pixels down one.
+ SetToCanvas<5, 5>(&canvas, initial_values);
+ gfx::ScrollCanvas(&canvas, center_three, gfx::Point(0, 1));
+ uint8 scroll_down_expected[kCanvasHeight][kCanvasWidth] = {
+ { 0x00, 0x01, 0x02, 0x03, 0x04 },
+ { 0x10, 0x11, 0x12, 0x13, 0x14 },
+ { 0x20, 0x11, 0x12, 0x13, 0x24 },
+ { 0x30, 0x21, 0x22, 0x23, 0x34 },
+ { 0x40, 0x41, 0x42, 0x43, 0x44 }};
+ VerifyCanvasValues<5, 5>(&canvas, scroll_down_expected);
+
+ // Reset and scroll the center 3 pixels right one.
+ SetToCanvas<5, 5>(&canvas, initial_values);
+ gfx::ScrollCanvas(&canvas, center_three, gfx::Point(1, 0));
+ uint8 scroll_right_expected[kCanvasHeight][kCanvasWidth] = {
+ { 0x00, 0x01, 0x02, 0x03, 0x04 },
+ { 0x10, 0x11, 0x11, 0x12, 0x14 },
+ { 0x20, 0x21, 0x21, 0x22, 0x24 },
+ { 0x30, 0x31, 0x31, 0x32, 0x34 },
+ { 0x40, 0x41, 0x42, 0x43, 0x44 }};
+ VerifyCanvasValues<5, 5>(&canvas, scroll_right_expected);
+
+ // Reset and scroll the center 3 pixels left one.
+ SetToCanvas<5, 5>(&canvas, initial_values);
+ gfx::ScrollCanvas(&canvas, center_three, gfx::Point(-1, 0));
+ uint8 scroll_left_expected[kCanvasHeight][kCanvasWidth] = {
+ { 0x00, 0x01, 0x02, 0x03, 0x04 },
+ { 0x10, 0x12, 0x13, 0x13, 0x14 },
+ { 0x20, 0x22, 0x23, 0x23, 0x24 },
+ { 0x30, 0x32, 0x33, 0x33, 0x34 },
+ { 0x40, 0x41, 0x42, 0x43, 0x44 }};
+ VerifyCanvasValues<5, 5>(&canvas, scroll_left_expected);
+
+ // Diagonal scroll.
+ SetToCanvas<5, 5>(&canvas, initial_values);
+ gfx::ScrollCanvas(&canvas, center_three, gfx::Point(2, 2));
+ uint8 scroll_diagonal_expected[kCanvasHeight][kCanvasWidth] = {
+ { 0x00, 0x01, 0x02, 0x03, 0x04 },
+ { 0x10, 0x11, 0x12, 0x13, 0x14 },
+ { 0x20, 0x21, 0x22, 0x23, 0x24 },
+ { 0x30, 0x31, 0x32, 0x11, 0x34 },
+ { 0x40, 0x41, 0x42, 0x43, 0x44 }};
+ VerifyCanvasValues<5, 5>(&canvas, scroll_diagonal_expected);
+}
diff --git a/ui/gfx/brush.h b/ui/gfx/brush.h
new file mode 100644
index 0000000..d6e92af
--- /dev/null
+++ b/ui/gfx/brush.h
@@ -0,0 +1,24 @@
+// 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.
+
+#ifndef UI_GFX_BRUSH_H_
+#define UI_GFX_BRUSH_H_
+#pragma once
+
+namespace gfx {
+
+// An object that encapsulates a platform native brush.
+// Subclasses handle memory management of the underlying native brush.
+class Brush {
+ public:
+ Brush() {}
+ virtual ~Brush() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Brush);
+};
+
+} // namespace gfx
+
+#endif // UI_GFX_BRUSH_H_
diff --git a/ui/gfx/canvas.cc b/ui/gfx/canvas.cc
new file mode 100644
index 0000000..28e6a8a
--- /dev/null
+++ b/ui/gfx/canvas.cc
@@ -0,0 +1,17 @@
+// 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"
+
+namespace gfx {
+
+CanvasSkia* Canvas::AsCanvasSkia() {
+ return NULL;
+}
+
+const CanvasSkia* Canvas::AsCanvasSkia() const {
+ return NULL;
+}
+
+} // namespace gfx;
diff --git a/ui/gfx/canvas.h b/ui/gfx/canvas.h
new file mode 100644
index 0000000..9517602
--- /dev/null
+++ b/ui/gfx/canvas.h
@@ -0,0 +1,238 @@
+// 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.
+
+#ifndef UI_GFX_CANVAS_H_
+#define UI_GFX_CANVAS_H_
+#pragma once
+
+#include <string>
+
+#include "base/string16.h"
+#include "gfx/native_widget_types.h"
+// TODO(beng): remove this include when we no longer depend on SkTypes.
+#include "skia/ext/platform_canvas.h"
+
+namespace gfx {
+
+class Brush;
+class CanvasSkia;
+class Font;
+class Point;
+class Rect;
+
+// TODO(beng): documentation.
+class Canvas {
+ 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,
+ };
+
+ virtual ~Canvas() {}
+
+ // Creates an empty canvas. Must be initialized before it can be used.
+ static Canvas* CreateCanvas();
+
+ // Creates a canvas with the specified size.
+ static Canvas* CreateCanvas(int width, int height, bool is_opaque);
+
+ // Saves a copy of the drawing state onto a stack, operating on this copy
+ // until a balanced call to Restore() is made.
+ virtual void Save() = 0;
+
+ // As with Save(), except draws to a layer that is blended with the canvas
+ // at the specified alpha once Restore() is called.
+ // |layer_bounds| are the bounds of the layer relative to the current
+ // transform.
+ virtual void SaveLayerAlpha(uint8 alpha) = 0;
+ virtual void SaveLayerAlpha(uint8 alpha, const gfx::Rect& layer_bounds) = 0;
+
+ // Restores the drawing state after a call to Save*(). It is an error to
+ // call Restore() more times than Save*().
+ virtual void Restore() = 0;
+
+ // Wrapper function that takes integer arguments.
+ // Returns true if the clip is non-empty.
+ // See clipRect for specifics.
+ virtual bool ClipRectInt(int x, int y, int w, int h) = 0;
+
+ // Wrapper function that takes integer arguments.
+ // See translate() for specifics.
+ virtual void TranslateInt(int x, int y) = 0;
+
+ // Wrapper function that takes integer arguments.
+ // See scale() for specifics.
+ virtual void ScaleInt(int x, int y) = 0;
+
+ // Fills the specified region with the specified color using a transfer
+ // mode of SkXfermode::kSrcOver_Mode.
+ virtual void FillRectInt(const SkColor& color,
+ int x, int y, int w, int h) = 0;
+
+ // Fills the specified region with the specified color and mode
+ virtual void FillRectInt(const SkColor& color,
+ int x, int y, int w, int h,
+ SkXfermode::Mode mode) = 0;
+
+ // Fills the specified region with the specified brush.
+ virtual void FillRectInt(const gfx::Brush* brush,
+ int x, int y, int w, int h) = 0;
+
+ // 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 DrawLineInt.
+ virtual void DrawRectInt(const SkColor& color,
+ int x, int y, int w, int h) = 0;
+
+ // 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 DrawLineInt.
+ virtual void DrawRectInt(const SkColor& color,
+ int x, int y, int w, int h,
+ SkXfermode::Mode mode) = 0;
+
+ // Draws the given rectangle with the given paint's parameters.
+ virtual void DrawRectInt(int x, int y, int w, int h,
+ const SkPaint& paint) = 0;
+
+ // Draws a single pixel line with the specified color.
+ virtual void DrawLineInt(const SkColor& color,
+ int x1, int y1,
+ int x2, int y2) = 0;
+
+ // Draws a bitmap with the origin at the specified location. The upper left
+ // corner of the bitmap is rendered at the specified location.
+ virtual void DrawBitmapInt(const SkBitmap& bitmap, int x, int y) = 0;
+
+ // 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.
+ virtual void DrawBitmapInt(const SkBitmap& bitmap,
+ int x, int y,
+ const SkPaint& paint) = 0;
+
+ // 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.
+ virtual 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) = 0;
+ virtual 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) = 0;
+
+ // 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.
+ virtual void DrawStringInt(const string16& text, const
+ gfx::Font& font,
+ const SkColor& color,
+ int x, int y, int w, int h) = 0;
+ virtual void DrawStringInt(const string16& text,
+ const gfx::Font& font,
+ const SkColor& color,
+ const gfx::Rect& display_rect) = 0;
+
+ // 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.
+ virtual void DrawStringInt(const string16& text,
+ const gfx::Font& font,
+ const SkColor& color,
+ int x, int y, int w, int h,
+ int flags) = 0;
+
+ // Draws a dotted gray rectangle used for focus purposes.
+ virtual void DrawFocusRect(int x, int y, int width, int height) = 0;
+
+ // Tiles the image in the specified region.
+ virtual void TileImageInt(const SkBitmap& bitmap,
+ int x, int y, int w, int h) = 0;
+ virtual void TileImageInt(const SkBitmap& bitmap,
+ int src_x, int src_y,
+ int dest_x, int dest_y, int w, int h) = 0;
+
+ // Returns a native drawing context for platform specific drawing routines to
+ // use. Must be balanced by a call to EndPlatformPaint().
+ virtual gfx::NativeDrawingContext BeginPlatformPaint() = 0;
+
+ // Signifies the end of platform drawing using the native drawing context
+ // returned by BeginPlatformPaint().
+ virtual void EndPlatformPaint() = 0;
+
+ // TODO(beng): remove this once we don't need to use any skia-specific methods
+ // through this interface.
+ // A quick and dirty way to obtain the underlying SkCanvas.
+ virtual CanvasSkia* AsCanvasSkia();
+ virtual const CanvasSkia* AsCanvasSkia() const;
+};
+
+class CanvasPaint {
+ public:
+ virtual ~CanvasPaint() {}
+
+ // Creates a canvas that paints to |view| when it is destroyed. The canvas is
+ // sized to the client area of |view|.
+ static CanvasPaint* CreateCanvasPaint(gfx::NativeView view);
+
+ // Returns true if the canvas has an invalid rect that needs to be repainted.
+ virtual bool IsValid() const = 0;
+
+ // Returns the rectangle that is invalid.
+ virtual gfx::Rect GetInvalidRect() const = 0;
+
+ // Returns the underlying Canvas.
+ virtual Canvas* AsCanvas() = 0;
+};
+
+} // namespace gfx;
+
+#endif // UI_GFX_CANVAS_H_
diff --git a/ui/gfx/canvas_direct2d.cc b/ui/gfx/canvas_direct2d.cc
new file mode 100644
index 0000000..cba7b64
--- /dev/null
+++ b/ui/gfx/canvas_direct2d.cc
@@ -0,0 +1,362 @@
+// 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_direct2d.h"
+
+#include "base/scoped_ptr.h"
+#include "gfx/brush.h"
+#include "gfx/rect.h"
+
+namespace {
+
+// Converts a SkColor to a ColorF.
+D2D1_COLOR_F SkColorToColorF(SkColor color) {
+ return D2D1::ColorF(static_cast<float>(SkColorGetR(color)) / 0xFF,
+ static_cast<float>(SkColorGetG(color)) / 0xFF,
+ static_cast<float>(SkColorGetB(color)) / 0xFF,
+ static_cast<float>(SkColorGetA(color)) / 0xFF);
+}
+
+D2D1_RECT_F RectToRectF(int x, int y, int w, int h) {
+ return D2D1::RectF(static_cast<float>(x), static_cast<float>(y),
+ static_cast<float>(x + w), static_cast<float>(y + h));
+}
+
+D2D1_RECT_F RectToRectF(const gfx::Rect& rect) {
+ return RectToRectF(rect.x(), rect.y(), rect.width(), rect.height());
+}
+
+D2D1_POINT_2F PointToPoint2F(int x, int y) {
+ return D2D1::Point2F(static_cast<float>(x), static_cast<float>(y));
+}
+
+D2D1_BITMAP_INTERPOLATION_MODE FilterToInterpolationMode(bool filter) {
+ return filter ? D2D1_BITMAP_INTERPOLATION_MODE_LINEAR
+ : D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR;
+}
+
+// Creates a Direct2D bitmap object from the contents of a SkBitmap. The caller
+// is responsible for releasing this object.
+ID2D1Bitmap* CreateD2D1BitmapFromSkBitmap(ID2D1RenderTarget* render_target,
+ const SkBitmap& bitmap) {
+ ID2D1Bitmap* d2d1_bitmap = NULL;
+ HRESULT hr = render_target->CreateBitmap(
+ D2D1::SizeU(bitmap.width(), bitmap.height()),
+ NULL,
+ NULL,
+ D2D1::BitmapProperties(
+ D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM,
+ D2D1_ALPHA_MODE_IGNORE)),
+ &d2d1_bitmap);
+ if (FAILED(hr))
+ return NULL;
+ bitmap.lockPixels();
+ d2d1_bitmap->CopyFromMemory(NULL, bitmap.getPixels(), bitmap.rowBytes());
+ bitmap.unlockPixels();
+ return d2d1_bitmap;
+}
+
+// Creates a Direct2D bitmap brush from the contents of a SkBitmap. The caller
+// is responsible for releasing this object.
+ID2D1Brush* CreateD2D1BrushFromSkBitmap(ID2D1RenderTarget* render_target,
+ const SkBitmap& bitmap,
+ D2D1_EXTEND_MODE extend_mode_x,
+ D2D1_EXTEND_MODE extend_mode_y) {
+ ScopedComPtr<ID2D1Bitmap> d2d1_bitmap(
+ CreateD2D1BitmapFromSkBitmap(render_target, bitmap));
+
+ ID2D1BitmapBrush* brush = NULL;
+ render_target->CreateBitmapBrush(
+ d2d1_bitmap,
+ D2D1::BitmapBrushProperties(extend_mode_x, extend_mode_y),
+ D2D1::BrushProperties(),
+ &brush);
+ return brush;
+}
+
+// A platform wrapper for a Direct2D brush that makes sure the underlying
+// ID2D1Brush COM object is released when this object is destroyed.
+class Direct2DBrush : public gfx::Brush {
+ public:
+ explicit Direct2DBrush(ID2D1Brush* brush) : brush_(brush) {
+ }
+
+ ID2D1Brush* brush() const { return brush_.get(); }
+
+ private:
+ ScopedComPtr<ID2D1Brush> brush_;
+
+ DISALLOW_COPY_AND_ASSIGN(Direct2DBrush);
+};
+
+
+} // namespace
+
+namespace gfx {
+
+// static
+ID2D1Factory* CanvasDirect2D::d2d1_factory_ = NULL;
+
+////////////////////////////////////////////////////////////////////////////////
+// CanvasDirect2D, public:
+
+CanvasDirect2D::CanvasDirect2D(ID2D1RenderTarget* rt) : rt_(rt) {
+ // A RenderState entry is pushed onto the stack to track the clip count prior
+ // to any calls to Save*().
+ state_.push(RenderState());
+ rt_->BeginDraw();
+}
+
+CanvasDirect2D::~CanvasDirect2D() {
+ // Unwind any clips that were pushed outside of any Save*()/Restore() pairs.
+ int clip_count = state_.top().clip_count;
+ for (int i = 0; i < clip_count; ++i)
+ rt_->PopAxisAlignedClip();
+ rt_->EndDraw();
+}
+
+// static
+ID2D1Factory* CanvasDirect2D::GetD2D1Factory() {
+ if (!d2d1_factory_)
+ D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &d2d1_factory_);
+ return d2d1_factory_;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// CanvasDirect2D, Canvas implementation:
+
+void CanvasDirect2D::Save() {
+ SaveInternal(NULL);
+}
+
+void CanvasDirect2D::SaveLayerAlpha(uint8 alpha) {
+ SaveLayerAlpha(alpha, gfx::Rect());
+}
+
+void CanvasDirect2D::SaveLayerAlpha(uint8 alpha,
+ const gfx::Rect& layer_bounds) {
+ D2D1_RECT_F bounds = D2D1::InfiniteRect();
+ if (!layer_bounds.IsEmpty())
+ bounds = RectToRectF(layer_bounds);
+ ID2D1Layer* layer = NULL;
+ HRESULT hr = rt_->CreateLayer(NULL, &layer);
+ if (SUCCEEDED(hr)) {
+ rt_->PushLayer(D2D1::LayerParameters(bounds,
+ NULL,
+ D2D1_ANTIALIAS_MODE_PER_PRIMITIVE,
+ D2D1::IdentityMatrix(),
+ static_cast<float>(alpha) / 0xFF,
+ NULL,
+ D2D1_LAYER_OPTIONS_NONE),
+ layer);
+ }
+ SaveInternal(layer);
+}
+
+void CanvasDirect2D::Restore() {
+ ID2D1Layer* layer = state_.top().layer;
+ if (layer) {
+ rt_->PopLayer();
+ layer->Release();
+ }
+
+ int clip_count = state_.top().clip_count;
+ for (int i = 0; i < clip_count; ++i)
+ rt_->PopAxisAlignedClip();
+
+ state_.pop();
+ // The state_ stack should never be empty - we should always have at least one
+ // entry to hold a clip count when there is no active save/restore entry.
+ CHECK(!state_.empty()) << "Called Restore() once too often!";
+
+ rt_->RestoreDrawingState(drawing_state_block_);
+}
+
+bool CanvasDirect2D::ClipRectInt(int x, int y, int w, int h) {
+ rt_->PushAxisAlignedClip(RectToRectF(x, y, w, h),
+ D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
+ // Increment the clip count so the call to PushAxisAlignedClip() can be
+ // balanced with a call to PopAxisAlignedClip in the next Restore().
+ ++state_.top().clip_count;
+ return w > 0 && h > 0;
+}
+
+void CanvasDirect2D::TranslateInt(int x, int y) {
+ D2D1_MATRIX_3X2_F raw;
+ rt_->GetTransform(&raw);
+ D2D1::Matrix3x2F transform(raw._11, raw._12, raw._21, raw._22, raw._31,
+ raw._32);
+ transform = D2D1::Matrix3x2F::Translation(static_cast<float>(x),
+ static_cast<float>(y)) * transform;
+ rt_->SetTransform(transform);
+}
+
+void CanvasDirect2D::ScaleInt(int x, int y) {
+ D2D1_MATRIX_3X2_F raw;
+ rt_->GetTransform(&raw);
+ D2D1::Matrix3x2F transform(raw._11, raw._12, raw._21, raw._22, raw._31,
+ raw._32);
+ transform = D2D1::Matrix3x2F::Scale(static_cast<float>(x),
+ static_cast<float>(y)) * transform;
+ rt_->SetTransform(transform);
+}
+
+void CanvasDirect2D::FillRectInt(const SkColor& color,
+ int x, int y, int w, int h) {
+ ScopedComPtr<ID2D1SolidColorBrush> solid_brush;
+ rt_->CreateSolidColorBrush(SkColorToColorF(color), solid_brush.Receive());
+ rt_->FillRectangle(RectToRectF(x, y, w, h), solid_brush);
+}
+
+void CanvasDirect2D::FillRectInt(const SkColor& color,
+ int x, int y, int w, int h,
+ SkXfermode::Mode mode) {
+ NOTIMPLEMENTED();
+}
+
+void CanvasDirect2D::FillRectInt(const gfx::Brush* brush,
+ int x, int y, int w, int h) {
+ const Direct2DBrush* d2d_brush = static_cast<const Direct2DBrush*>(brush);
+ rt_->FillRectangle(RectToRectF(x, y, w, h), d2d_brush->brush());
+}
+
+void CanvasDirect2D::DrawRectInt(const SkColor& color,
+ int x, int y, int w, int h) {
+ ScopedComPtr<ID2D1SolidColorBrush> solid_brush;
+ rt_->CreateSolidColorBrush(SkColorToColorF(color), solid_brush.Receive());
+ rt_->DrawRectangle(RectToRectF(x, y, w, h), solid_brush);
+}
+
+void CanvasDirect2D::DrawRectInt(const SkColor& color,
+ int x, int y, int w, int h,
+ SkXfermode::Mode mode) {
+ NOTIMPLEMENTED();
+}
+
+void CanvasDirect2D::DrawRectInt(int x, int y, int w, int h,
+ const SkPaint& paint) {
+ NOTIMPLEMENTED();
+}
+
+void CanvasDirect2D::DrawLineInt(const SkColor& color,
+ int x1, int y1,
+ int x2, int y2) {
+ ScopedComPtr<ID2D1SolidColorBrush> solid_brush;
+ rt_->CreateSolidColorBrush(SkColorToColorF(color), solid_brush.Receive());
+ rt_->DrawLine(PointToPoint2F(x1, y1), PointToPoint2F(x2, y2), solid_brush);
+}
+
+void CanvasDirect2D::DrawBitmapInt(const SkBitmap& bitmap, int x, int y) {
+ ScopedComPtr<ID2D1Bitmap> d2d1_bitmap(
+ CreateD2D1BitmapFromSkBitmap(rt_, bitmap));
+ rt_->DrawBitmap(d2d1_bitmap,
+ RectToRectF(x, y, bitmap.width(), bitmap.height()),
+ 1.0f,
+ D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR,
+ RectToRectF(0, 0, bitmap.width(), bitmap.height()));
+}
+
+void CanvasDirect2D::DrawBitmapInt(const SkBitmap& bitmap,
+ int x, int y,
+ const SkPaint& paint) {
+ NOTIMPLEMENTED();
+}
+
+void CanvasDirect2D::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) {
+ ScopedComPtr<ID2D1Bitmap> d2d1_bitmap(
+ CreateD2D1BitmapFromSkBitmap(rt_, bitmap));
+ rt_->DrawBitmap(d2d1_bitmap,
+ RectToRectF(dest_x, dest_y, dest_w, dest_h),
+ 1.0f,
+ FilterToInterpolationMode(filter),
+ RectToRectF(src_x, src_y, src_w, src_h));
+}
+
+void CanvasDirect2D::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) {
+ NOTIMPLEMENTED();
+}
+
+void CanvasDirect2D::DrawStringInt(const string16& text,
+ const gfx::Font& font,
+ const SkColor& color,
+ int x, int y, int w, int h) {
+ NOTIMPLEMENTED();
+}
+
+void CanvasDirect2D::DrawStringInt(const string16& text,
+ const gfx::Font& font,
+ const SkColor& color,
+ const gfx::Rect& display_rect) {
+ NOTIMPLEMENTED();
+}
+
+void CanvasDirect2D::DrawStringInt(const string16& text,
+ const gfx::Font& font,
+ const SkColor& color,
+ int x, int y, int w, int h,
+ int flags) {
+ NOTIMPLEMENTED();
+}
+
+void CanvasDirect2D::DrawFocusRect(int x, int y, int width, int height) {
+ NOTIMPLEMENTED();
+}
+
+void CanvasDirect2D::TileImageInt(const SkBitmap& bitmap,
+ int x, int y, int w, int h) {
+ ScopedComPtr<ID2D1Brush> brush(
+ CreateD2D1BrushFromSkBitmap(rt_, bitmap, D2D1_EXTEND_MODE_WRAP,
+ D2D1_EXTEND_MODE_WRAP));
+ rt_->FillRectangle(RectToRectF(x, y, w, h), brush);
+}
+
+void CanvasDirect2D::TileImageInt(const SkBitmap& bitmap,
+ int src_x, int src_y,
+ int dest_x, int dest_y, int w, int h) {
+ NOTIMPLEMENTED();
+}
+
+gfx::NativeDrawingContext CanvasDirect2D::BeginPlatformPaint() {
+ DCHECK(!interop_rt_.get());
+ interop_rt_.QueryFrom(rt_);
+ HDC dc = NULL;
+ if (interop_rt_.get())
+ interop_rt_->GetDC(D2D1_DC_INITIALIZE_MODE_COPY, &dc);
+ return dc;
+}
+
+void CanvasDirect2D::EndPlatformPaint() {
+ DCHECK(interop_rt_.get());
+ interop_rt_->ReleaseDC(NULL);
+ interop_rt_.release();
+}
+
+CanvasSkia* CanvasDirect2D::AsCanvasSkia() {
+ return NULL;
+}
+
+const CanvasSkia* CanvasDirect2D::AsCanvasSkia() const {
+ return NULL;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// CanvasDirect2D, private:
+
+void CanvasDirect2D::SaveInternal(ID2D1Layer* layer) {
+ if (!drawing_state_block_)
+ GetD2D1Factory()->CreateDrawingStateBlock(drawing_state_block_.Receive());
+ rt_->SaveDrawingState(drawing_state_block_.get());
+ state_.push(RenderState(layer));
+}
+
+} // namespace gfx
diff --git a/ui/gfx/canvas_direct2d.h b/ui/gfx/canvas_direct2d.h
new file mode 100644
index 0000000..101688a
--- /dev/null
+++ b/ui/gfx/canvas_direct2d.h
@@ -0,0 +1,112 @@
+// 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.
+
+#ifndef UI_GFX_CANVAS_DIRECT2D_H_
+#define UI_GFX_CANVAS_DIRECT2D_H_
+#pragma once
+
+#include <d2d1.h>
+
+#include <stack>
+
+#include "base/scoped_comptr_win.h"
+#include "gfx/canvas.h"
+
+namespace gfx {
+
+class CanvasDirect2D : public Canvas {
+ public:
+ // Creates an empty Canvas.
+ explicit CanvasDirect2D(ID2D1RenderTarget* rt);
+ virtual ~CanvasDirect2D();
+
+ // Retrieves the application's D2D1 Factory.
+ static ID2D1Factory* GetD2D1Factory();
+
+ // Overridden from Canvas:
+ virtual void Save();
+ virtual void SaveLayerAlpha(uint8 alpha);
+ virtual void SaveLayerAlpha(uint8 alpha, const gfx::Rect& layer_bounds);
+ virtual void Restore();
+ virtual bool ClipRectInt(int x, int y, int w, int h);
+ virtual void TranslateInt(int x, int y);
+ virtual void ScaleInt(int x, int y);
+ virtual void FillRectInt(const SkColor& color, int x, int y, int w, int h);
+ virtual void FillRectInt(const SkColor& color, int x, int y, int w, int h,
+ SkXfermode::Mode mode);
+ virtual void FillRectInt(const gfx::Brush* brush, int x, int y, int w, int h);
+ virtual void DrawRectInt(const SkColor& color, int x, int y, int w, int h);
+ virtual void DrawRectInt(const SkColor& color,
+ int x, int y, int w, int h,
+ SkXfermode::Mode mode);
+ virtual void DrawRectInt(int x, int y, int w, int h, const SkPaint& paint);
+ virtual void DrawLineInt(const SkColor& color,
+ int x1, int y1,
+ int x2, int y2);
+ virtual void DrawBitmapInt(const SkBitmap& bitmap, int x, int y);
+ virtual void DrawBitmapInt(const SkBitmap& bitmap,
+ int x, int y,
+ const SkPaint& paint);
+ virtual 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);
+ virtual 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);
+ virtual void DrawStringInt(const string16& text,
+ const gfx::Font& font,
+ const SkColor& color,
+ int x, int y, int w, int h);
+ virtual void DrawStringInt(const string16& text,
+ const gfx::Font& font,
+ const SkColor& color,
+ const gfx::Rect& display_rect);
+ virtual void DrawStringInt(const string16& text,
+ const gfx::Font& font,
+ const SkColor& color,
+ int x, int y, int w, int h,
+ int flags);
+ 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,
+ int src_x, int src_y,
+ int dest_x, int dest_y, int w, int h);
+ virtual gfx::NativeDrawingContext BeginPlatformPaint();
+ virtual void EndPlatformPaint();
+ virtual CanvasSkia* AsCanvasSkia();
+ virtual const CanvasSkia* AsCanvasSkia() const;
+
+ private:
+ void SaveInternal(ID2D1Layer* layer);
+
+ ID2D1RenderTarget* rt_;
+ ScopedComPtr<ID2D1GdiInteropRenderTarget> interop_rt_;
+ ScopedComPtr<ID2D1DrawingStateBlock> drawing_state_block_;
+ static ID2D1Factory* d2d1_factory_;
+
+ // Every time Save* is called, a RenderState object is pushed onto the
+ // RenderState stack.
+ struct RenderState {
+ explicit RenderState(ID2D1Layer* layer) : layer(layer), clip_count(0) {}
+ RenderState() : layer(NULL), clip_count(0) {}
+
+ // A D2D layer associated with this state, or NULL if there is no layer.
+ // The layer is created and owned by the Canvas.
+ ID2D1Layer* layer;
+ // The number of clip operations performed. This is used to balance calls to
+ // PushAxisAlignedClip with calls to PopAxisAlignedClip when Restore() is
+ // called.
+ int clip_count;
+ };
+ std::stack<RenderState> state_;
+
+ DISALLOW_COPY_AND_ASSIGN(CanvasDirect2D);
+};
+
+} // namespace gfx;
+
+#endif // UI_GFX_CANVAS_DIRECT2D_H_
diff --git a/ui/gfx/canvas_direct2d_unittest.cc b/ui/gfx/canvas_direct2d_unittest.cc
new file mode 100644
index 0000000..8884f32
--- /dev/null
+++ b/ui/gfx/canvas_direct2d_unittest.cc
@@ -0,0 +1,350 @@
+// 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 <windows.h>
+#include <uxtheme.h>
+#include <vsstyle.h>
+#include <vssym32.h>
+
+#include "base/command_line.h"
+#include "base/ref_counted_memory.h"
+#include "base/resource_util.h"
+#include "base/scoped_ptr.h"
+#include "gfx/brush.h"
+#include "gfx/canvas_direct2d.h"
+#include "gfx/canvas_skia.h"
+#include "gfx/codec/png_codec.h"
+#include "gfx/native_theme_win.h"
+#include "gfx/rect.h"
+#include "gfx/win_util.h"
+#include "grit/gfx_resources.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+
+namespace {
+
+const char kVisibleModeFlag[] = "d2d-canvas-visible";
+const wchar_t kWindowClassName[] = L"GFXD2DTestWindowClass";
+
+class TestWindow {
+ public:
+ static const int kWindowSize = 500;
+ static const int kWindowPosition = 10;
+
+ TestWindow() : hwnd_(NULL) {
+ if (CommandLine::ForCurrentProcess()->HasSwitch(kVisibleModeFlag))
+ Sleep(1000);
+
+ RegisterMyClass();
+
+ hwnd_ = CreateWindowEx(0, kWindowClassName, NULL,
+ WS_OVERLAPPEDWINDOW,
+ kWindowPosition, kWindowPosition,
+ kWindowSize, kWindowSize,
+ NULL, NULL, NULL, this);
+ DCHECK(hwnd_);
+
+ // Initialize the RenderTarget for the window.
+ rt_ = MakeHWNDRenderTarget();
+
+ if (CommandLine::ForCurrentProcess()->HasSwitch(kVisibleModeFlag))
+ ShowWindow(hwnd(), SW_SHOW);
+ }
+ virtual ~TestWindow() {
+ if (CommandLine::ForCurrentProcess()->HasSwitch(kVisibleModeFlag))
+ Sleep(1000);
+ DestroyWindow(hwnd());
+ UnregisterMyClass();
+ }
+
+ HWND hwnd() const { return hwnd_; }
+
+ ID2D1RenderTarget* rt() const { return rt_.get(); }
+
+ private:
+ ID2D1RenderTarget* MakeHWNDRenderTarget() {
+ D2D1_RENDER_TARGET_PROPERTIES rt_properties =
+ D2D1::RenderTargetProperties();
+ rt_properties.usage = D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE;
+
+ ID2D1HwndRenderTarget* rt = NULL;
+ gfx::CanvasDirect2D::GetD2D1Factory()->CreateHwndRenderTarget(
+ rt_properties,
+ D2D1::HwndRenderTargetProperties(hwnd(), D2D1::SizeU(500, 500)),
+ &rt);
+ return rt;
+ }
+
+ void RegisterMyClass() {
+ WNDCLASSEX class_ex;
+ class_ex.cbSize = sizeof(WNDCLASSEX);
+ class_ex.style = CS_DBLCLKS;
+ class_ex.lpfnWndProc = &DefWindowProc;
+ class_ex.cbClsExtra = 0;
+ class_ex.cbWndExtra = 0;
+ class_ex.hInstance = NULL;
+ class_ex.hIcon = NULL;
+ class_ex.hCursor = LoadCursor(NULL, IDC_ARROW);
+ class_ex.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_BACKGROUND);
+ class_ex.lpszMenuName = NULL;
+ class_ex.lpszClassName = kWindowClassName;
+ class_ex.hIconSm = class_ex.hIcon;
+ ATOM atom = RegisterClassEx(&class_ex);
+ DCHECK(atom);
+ }
+
+ void UnregisterMyClass() {
+ ::UnregisterClass(kWindowClassName, NULL);
+ }
+
+ HWND hwnd_;
+
+ ScopedComPtr<ID2D1RenderTarget> rt_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestWindow);
+};
+
+// Loads a png data blob from the data resources associated with this
+// executable, decodes it and returns a SkBitmap.
+SkBitmap LoadBitmapFromResources(int resource_id) {
+ SkBitmap bitmap;
+
+ HINSTANCE resource_instance = GetModuleHandle(NULL);
+ void* data_ptr;
+ size_t data_size;
+ if (base::GetDataResourceFromModule(resource_instance, resource_id, &data_ptr,
+ &data_size)) {
+ scoped_refptr<RefCountedMemory> memory(new RefCountedStaticMemory(
+ reinterpret_cast<const unsigned char*>(data_ptr), data_size));
+ if (!memory)
+ return bitmap;
+
+ if (!gfx::PNGCodec::Decode(memory->front(), memory->size(), &bitmap))
+ NOTREACHED() << "Unable to decode theme image resource " << resource_id;
+ }
+ return bitmap;
+}
+
+bool CheckForD2DCompatibility() {
+ if (!gfx::Direct2dIsAvailable()) {
+ LOG(WARNING) << "Test is disabled as it requires either Windows 7 or " <<
+ "Vista with Platform Update KB971644";
+ return false;
+ }
+ return true;
+}
+
+} // namespace
+
+TEST(CanvasDirect2D, CreateCanvas) {
+ if (!CheckForD2DCompatibility())
+ return;
+ TestWindow window;
+ gfx::CanvasDirect2D canvas(window.rt());
+}
+
+TEST(CanvasDirect2D, SaveRestoreNesting) {
+ if (!CheckForD2DCompatibility())
+ return;
+ TestWindow window;
+ gfx::CanvasDirect2D canvas(window.rt());
+
+ // Simple.
+ canvas.Save();
+ canvas.Restore();
+
+ // Nested.
+ canvas.Save();
+ canvas.Save();
+ canvas.Restore();
+ canvas.Restore();
+
+ // Simple alpha.
+ canvas.SaveLayerAlpha(127);
+ canvas.Restore();
+
+ // Alpha with sub-rect.
+ canvas.SaveLayerAlpha(127, gfx::Rect(20, 20, 100, 100));
+ canvas.Restore();
+
+ // Nested alpha.
+ canvas.Save();
+ canvas.SaveLayerAlpha(127);
+ canvas.Save();
+ canvas.Restore();
+ canvas.Restore();
+ canvas.Restore();
+}
+
+TEST(CanvasDirect2D, SaveLayerAlpha) {
+ if (!CheckForD2DCompatibility())
+ return;
+ TestWindow window;
+ gfx::CanvasDirect2D canvas(window.rt());
+
+ canvas.Save();
+ canvas.FillRectInt(SK_ColorBLUE, 20, 20, 100, 100);
+ canvas.SaveLayerAlpha(127);
+ canvas.FillRectInt(SK_ColorRED, 60, 60, 100, 100);
+ canvas.Restore();
+ canvas.Restore();
+}
+
+TEST(CanvasDirect2D, SaveLayerAlphaWithBounds) {
+ if (!CheckForD2DCompatibility())
+ return;
+ TestWindow window;
+ gfx::CanvasDirect2D canvas(window.rt());
+
+ canvas.Save();
+ canvas.FillRectInt(SK_ColorBLUE, 20, 20, 100, 100);
+ canvas.SaveLayerAlpha(127, gfx::Rect(60, 60, 50, 50));
+ canvas.FillRectInt(SK_ColorRED, 60, 60, 100, 100);
+ canvas.Restore();
+ canvas.Restore();
+}
+
+TEST(CanvasDirect2D, FillRect) {
+ if (!CheckForD2DCompatibility())
+ return;
+ TestWindow window;
+ gfx::CanvasDirect2D canvas(window.rt());
+
+ canvas.FillRectInt(SK_ColorRED, 20, 20, 100, 100);
+}
+
+TEST(CanvasDirect2D, PlatformPainting) {
+ if (!CheckForD2DCompatibility())
+ return;
+ TestWindow window;
+ gfx::CanvasDirect2D canvas(window.rt());
+
+ gfx::NativeDrawingContext dc = canvas.BeginPlatformPaint();
+
+ // Use the system theme engine to draw a native button. This only works on a
+ // GDI device context.
+ RECT r = { 20, 20, 220, 80 };
+ gfx::NativeTheme::instance()->PaintButton(
+ dc, BP_PUSHBUTTON, PBS_NORMAL, DFCS_BUTTONPUSH, &r);
+
+ canvas.EndPlatformPaint();
+}
+
+TEST(CanvasDirect2D, ClipRect) {
+ if (!CheckForD2DCompatibility())
+ return;
+ TestWindow window;
+ gfx::CanvasDirect2D canvas(window.rt());
+
+ canvas.FillRectInt(SK_ColorGREEN, 0, 0, 500, 500);
+ canvas.ClipRectInt(20, 20, 120, 120);
+ canvas.FillRectInt(SK_ColorBLUE, 0, 0, 500, 500);
+}
+
+TEST(CanvasDirect2D, ClipRectWithTranslate) {
+ if (!CheckForD2DCompatibility())
+ return;
+ TestWindow window;
+ gfx::CanvasDirect2D canvas(window.rt());
+
+ // Repeat the same rendering as in ClipRect...
+ canvas.Save();
+ canvas.FillRectInt(SK_ColorGREEN, 0, 0, 500, 500);
+ canvas.ClipRectInt(20, 20, 120, 120);
+ canvas.FillRectInt(SK_ColorBLUE, 0, 0, 500, 500);
+ canvas.Restore();
+
+ // ... then translate, clip and fill again relative to the new origin.
+ canvas.Save();
+ canvas.TranslateInt(150, 150);
+ canvas.ClipRectInt(10, 10, 110, 110);
+ canvas.FillRectInt(SK_ColorRED, 0, 0, 500, 500);
+ canvas.Restore();
+}
+
+TEST(CanvasDirect2D, ClipRectWithScale) {
+ if (!CheckForD2DCompatibility())
+ return;
+ TestWindow window;
+ gfx::CanvasDirect2D canvas(window.rt());
+
+ // Repeat the same rendering as in ClipRect...
+ canvas.Save();
+ canvas.FillRectInt(SK_ColorGREEN, 0, 0, 500, 500);
+ canvas.ClipRectInt(20, 20, 120, 120);
+ canvas.FillRectInt(SK_ColorBLUE, 0, 0, 500, 500);
+ canvas.Restore();
+
+ // ... then translate and scale, clip and fill again relative to the new
+ // origin.
+ canvas.Save();
+ canvas.TranslateInt(150, 150);
+ canvas.ScaleInt(2, 2);
+ canvas.ClipRectInt(10, 10, 110, 110);
+ canvas.FillRectInt(SK_ColorRED, 0, 0, 500, 500);
+ canvas.Restore();
+}
+
+TEST(CanvasDirect2D, DrawRectInt) {
+ if (!CheckForD2DCompatibility())
+ return;
+ TestWindow window;
+ gfx::CanvasDirect2D canvas(window.rt());
+
+ canvas.Save();
+ canvas.DrawRectInt(SK_ColorRED, 10, 10, 200, 200);
+ canvas.Restore();
+}
+
+TEST(CanvasDirect2D, DrawLineInt) {
+ if (!CheckForD2DCompatibility())
+ return;
+ TestWindow window;
+ gfx::CanvasDirect2D canvas(window.rt());
+
+ canvas.Save();
+ canvas.DrawLineInt(SK_ColorRED, 10, 10, 210, 210);
+ canvas.Restore();
+}
+
+TEST(CanvasDirect2D, DrawBitmapInt) {
+ if (!CheckForD2DCompatibility())
+ return;
+ TestWindow window;
+ gfx::CanvasDirect2D canvas(window.rt());
+
+ SkBitmap bitmap = LoadBitmapFromResources(IDR_BITMAP_BRUSH_IMAGE);
+
+ canvas.Save();
+ canvas.DrawBitmapInt(bitmap, 100, 100);
+ canvas.Restore();
+}
+
+TEST(CanvasDirect2D, DrawBitmapInt2) {
+ if (!CheckForD2DCompatibility())
+ return;
+ TestWindow window;
+ gfx::CanvasDirect2D canvas(window.rt());
+
+ SkBitmap bitmap = LoadBitmapFromResources(IDR_BITMAP_BRUSH_IMAGE);
+
+ canvas.Save();
+ canvas.DrawBitmapInt(bitmap, 5, 5, 30, 30, 10, 10, 30, 30, false);
+ canvas.DrawBitmapInt(bitmap, 5, 5, 30, 30, 110, 110, 100, 100, true);
+ canvas.DrawBitmapInt(bitmap, 5, 5, 30, 30, 220, 220, 100, 100, false);
+ canvas.Restore();
+}
+
+TEST(CanvasDirect2D, TileImageInt) {
+ if (!CheckForD2DCompatibility())
+ return;
+ TestWindow window;
+ gfx::CanvasDirect2D canvas(window.rt());
+
+ SkBitmap bitmap = LoadBitmapFromResources(IDR_BITMAP_BRUSH_IMAGE);
+
+ canvas.Save();
+ canvas.TileImageInt(bitmap, 10, 10, 300, 300);
+ canvas.Restore();
+}
diff --git a/ui/gfx/canvas_skia.cc b/ui/gfx/canvas_skia.cc
new file mode 100644
index 0000000..aa0cb9d
--- /dev/null
+++ b/ui/gfx/canvas_skia.cc
@@ -0,0 +1,391 @@
+// 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_skia.h"
+
+#include <limits>
+
+#include "base/i18n/rtl.h"
+#include "base/logging.h"
+#include "gfx/brush.h"
+#include "gfx/font.h"
+#include "gfx/rect.h"
+#include "third_party/skia/include/effects/SkGradientShader.h"
+
+#if defined(OS_WIN)
+#include "gfx/canvas_skia_paint.h"
+#endif
+
+namespace {
+
+// A platform wrapper for a Skia shader that makes sure the underlying
+// SkShader object is unref'ed when this object is destroyed.
+class SkiaShader : public gfx::Brush {
+ public:
+ explicit SkiaShader(SkShader* shader) : shader_(shader) {
+ }
+ virtual ~SkiaShader() {
+ shader_->unref();
+ }
+
+ // Accessor for shader, so it can be associated with a SkPaint.
+ SkShader* shader() const { return shader_; }
+
+ private:
+ SkShader* shader_;
+
+ DISALLOW_COPY_AND_ASSIGN(SkiaShader);
+};
+
+
+} // namespace
+
+namespace gfx {
+
+////////////////////////////////////////////////////////////////////////////////
+// CanvasSkia, public:
+
+// static
+int CanvasSkia::DefaultCanvasTextAlignment() {
+ if (!base::i18n::IsRTL())
+ return gfx::Canvas::TEXT_ALIGN_LEFT;
+ return gfx::Canvas::TEXT_ALIGN_RIGHT;
+}
+
+SkBitmap CanvasSkia::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 constructor, since we want an actual copy
+ // of the bitmap.
+ SkBitmap result;
+ device_bitmap.copyTo(&result, SkBitmap::kARGB_8888_Config);
+ return result;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// CanvasSkia, Canvas implementation:
+
+void CanvasSkia::Save() {
+ save();
+}
+
+void CanvasSkia::SaveLayerAlpha(uint8 alpha) {
+ saveLayerAlpha(NULL, alpha);
+}
+
+
+void CanvasSkia::SaveLayerAlpha(uint8 alpha, const gfx::Rect& layer_bounds) {
+ SkRect bounds;
+ bounds.set(SkIntToScalar(layer_bounds.x()),
+ SkIntToScalar(layer_bounds.y()),
+ SkIntToScalar(layer_bounds.right()),
+ SkIntToScalar(layer_bounds.bottom()));
+ saveLayerAlpha(&bounds, alpha);
+}
+
+void CanvasSkia::Restore() {
+ restore();
+}
+
+bool CanvasSkia::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);
+}
+
+void CanvasSkia::TranslateInt(int x, int y) {
+ translate(SkIntToScalar(x), SkIntToScalar(y));
+}
+
+void CanvasSkia::ScaleInt(int x, int y) {
+ scale(SkIntToScalar(x), SkIntToScalar(y));
+}
+
+void CanvasSkia::FillRectInt(const SkColor& color, int x, int y, int w, int h) {
+ FillRectInt(color, x, y, w, h, SkXfermode::kSrcOver_Mode);
+}
+
+void CanvasSkia::FillRectInt(const SkColor& color,
+ int x, int y, int w, int h,
+ SkXfermode::Mode mode) {
+ SkPaint paint;
+ paint.setColor(color);
+ paint.setStyle(SkPaint::kFill_Style);
+ paint.setXfermodeMode(mode);
+ DrawRectInt(x, y, w, h, paint);
+}
+
+void CanvasSkia::FillRectInt(const gfx::Brush* brush,
+ int x, int y, int w, int h) {
+ const SkiaShader* shader = static_cast<const SkiaShader*>(brush);
+ SkPaint paint;
+ paint.setShader(shader->shader());
+ // TODO(beng): set shader transform to match canvas transform.
+ DrawRectInt(x, y, w, h, paint);
+}
+
+void CanvasSkia::DrawRectInt(const SkColor& color, int x, int y, int w, int h) {
+ DrawRectInt(color, x, y, w, h, SkXfermode::kSrcOver_Mode);
+}
+
+void CanvasSkia::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);
+
+ DrawRectInt(x, y, w, h, paint);
+}
+
+void CanvasSkia::DrawRectInt(int x, int y, int w, int h, const SkPaint& paint) {
+ SkIRect rc = { x, y, x + w, y + h };
+ drawIRect(rc, paint);
+}
+
+void CanvasSkia::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 CanvasSkia::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();
+
+ DrawRectInt(x, y, width, 1, paint);
+ DrawRectInt(x, y + height - 1, width, 1, paint);
+ DrawRectInt(x, y, 1, height, paint);
+ DrawRectInt(x + width - 1, y, 1, height, paint);
+}
+
+void CanvasSkia::DrawBitmapInt(const SkBitmap& bitmap, int x, int y) {
+ drawBitmap(bitmap, SkIntToScalar(x), SkIntToScalar(y));
+}
+
+void CanvasSkia::DrawBitmapInt(const SkBitmap& bitmap,
+ int x, int y,
+ const SkPaint& paint) {
+ drawBitmap(bitmap, SkIntToScalar(x), SkIntToScalar(y), &paint);
+}
+
+void CanvasSkia::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 CanvasSkia::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 CanvasSkia::DrawStringInt(const string16& 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::CanvasSkia::DefaultCanvasTextAlignment());
+}
+
+void CanvasSkia::DrawStringInt(const string16& 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 CanvasSkia::TileImageInt(const SkBitmap& bitmap,
+ int x, int y, int w, int h) {
+ TileImageInt(bitmap, 0, 0, x, y, w, h);
+}
+
+void CanvasSkia::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();
+}
+
+gfx::NativeDrawingContext CanvasSkia::BeginPlatformPaint() {
+ return beginPlatformPaint();
+}
+
+void CanvasSkia::EndPlatformPaint() {
+ endPlatformPaint();
+}
+
+CanvasSkia* CanvasSkia::AsCanvasSkia() {
+ return this;
+}
+
+const CanvasSkia* CanvasSkia::AsCanvasSkia() const {
+ return this;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// CanvasSkia, private:
+
+bool CanvasSkia::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));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Canvas, public:
+
+Canvas* Canvas::CreateCanvas() {
+ return new CanvasSkia;
+}
+
+Canvas* Canvas::CreateCanvas(int width, int height, bool is_opaque) {
+ return new CanvasSkia(width, height, is_opaque);
+}
+
+#if defined(OS_WIN)
+// TODO(beng): move to canvas_win.cc, etc.
+class CanvasPaintWin : public CanvasSkiaPaint, public CanvasPaint {
+ public:
+ CanvasPaintWin(gfx::NativeView view) : CanvasSkiaPaint(view) {}
+
+ // Overridden from CanvasPaint2:
+ virtual bool IsValid() const {
+ return isEmpty();
+ }
+
+ virtual gfx::Rect GetInvalidRect() const {
+ return gfx::Rect(paintStruct().rcPaint);
+ }
+
+ virtual Canvas* AsCanvas() {
+ return this;
+ }
+};
+#endif
+
+CanvasPaint* CanvasPaint::CreateCanvasPaint(gfx::NativeView view) {
+#if defined(OS_WIN)
+ return new CanvasPaintWin(view);
+#else
+ return NULL;
+#endif
+}
+
+} // namespace gfx
diff --git a/ui/gfx/canvas_skia.h b/ui/gfx/canvas_skia.h
new file mode 100644
index 0000000..ce16fae
--- /dev/null
+++ b/ui/gfx/canvas_skia.h
@@ -0,0 +1,165 @@
+// 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.
+
+#ifndef UI_GFX_CANVAS_SKIA_H_
+#define UI_GFX_CANVAS_SKIA_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "base/string16.h"
+#include "gfx/canvas.h"
+#include "skia/ext/platform_canvas.h"
+
+#if defined(OS_POSIX) && !defined(OS_MACOSX)
+typedef struct _GdkPixbuf GdkPixbuf;
+#endif
+
+namespace gfx {
+
+class Canvas;
+
+// CanvasSkia 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 CanvasSkia : public skia::PlatformCanvas,
+ public Canvas {
+ public:
+ // Creates an empty Canvas. Callers must use initialize before using the
+ // canvas.
+ CanvasSkia();
+
+ CanvasSkia(int width, int height, bool is_opaque);
+
+ virtual ~CanvasSkia();
+
+ // 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 string16& text,
+ const gfx::Font& font,
+ int* width, int* height,
+ int flags);
+
+ // Returns the default text alignment to be used when drawing text on a
+ // gfx::CanvasSkia 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();
+
+#if defined(OS_POSIX) && !defined(OS_MACOSX)
+ // Draw the pixbuf in its natural size at (x, y).
+ void DrawGdkPixbuf(GdkPixbuf* pixbuf, int x, int y);
+#endif
+
+#if defined(OS_WIN) || (defined(OS_POSIX) && !defined(OS_MACOSX))
+ // Draws text with a 1-pixel halo around it of the given color.
+ // On Windows, 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.
+ // On Linux, text with halo is created by stroking it with 2px |halo_color|
+ // then filling it with |text_color|.
+ void DrawStringWithHalo(const string16& text,
+ const gfx::Font& font,
+ const SkColor& text_color,
+ const SkColor& halo_color,
+ int x, int y, int w, int h,
+ int flags);
+#endif
+
+ // Extracts a bitmap from the contents of this canvas.
+ SkBitmap ExtractBitmap() const;
+
+ // Overridden from Canvas:
+ virtual void Save();
+ virtual void SaveLayerAlpha(uint8 alpha);
+ virtual void SaveLayerAlpha(uint8 alpha, const gfx::Rect& layer_bounds);
+ virtual void Restore();
+ virtual bool ClipRectInt(int x, int y, int w, int h);
+ virtual void TranslateInt(int x, int y);
+ virtual void ScaleInt(int x, int y);
+ virtual void FillRectInt(const SkColor& color, int x, int y, int w, int h);
+ virtual void FillRectInt(const SkColor& color, int x, int y, int w, int h,
+ SkXfermode::Mode mode);
+ virtual void FillRectInt(const gfx::Brush* brush, int x, int y, int w, int h);
+ virtual void DrawRectInt(const SkColor& color, int x, int y, int w, int h);
+ virtual void DrawRectInt(const SkColor& color,
+ int x, int y, int w, int h,
+ SkXfermode::Mode mode);
+ virtual void DrawRectInt(int x, int y, int w, int h, const SkPaint& paint);
+ virtual void DrawLineInt(const SkColor& color,
+ int x1, int y1,
+ int x2, int y2);
+ virtual void DrawBitmapInt(const SkBitmap& bitmap, int x, int y);
+ virtual void DrawBitmapInt(const SkBitmap& bitmap,
+ int x, int y,
+ const SkPaint& paint);
+ virtual 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);
+ virtual 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);
+ virtual void DrawStringInt(const string16& text,
+ const gfx::Font& font,
+ const SkColor& color,
+ int x, int y, int w, int h);
+ virtual void DrawStringInt(const string16& text,
+ const gfx::Font& font,
+ const SkColor& color,
+ const gfx::Rect& display_rect);
+ virtual void DrawStringInt(const string16& text,
+ const gfx::Font& font,
+ const SkColor& color,
+ int x, int y, int w, int h,
+ int flags);
+ 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,
+ int src_x, int src_y,
+ int dest_x, int dest_y, int w, int h);
+ virtual gfx::NativeDrawingContext BeginPlatformPaint();
+ virtual void EndPlatformPaint();
+ virtual CanvasSkia* AsCanvasSkia();
+ virtual const CanvasSkia* AsCanvasSkia() const;
+
+ private:
+ // Test whether the provided rectangle intersects the current clip rect.
+ bool IntersectsClipRectInt(int x, int y, int w, int h);
+
+#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 string16& text,
+ HFONT font,
+ const SkColor& color,
+ int x, int y, int w, int h,
+ int flags);
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(CanvasSkia);
+};
+
+} // namespace gfx;
+
+#endif // UI_GFX_CANVAS_SKIA_H_
diff --git a/ui/gfx/canvas_skia_linux.cc b/ui/gfx/canvas_skia_linux.cc
new file mode 100644
index 0000000..ce2ae01
--- /dev/null
+++ b/ui/gfx/canvas_skia_linux.cc
@@ -0,0 +1,387 @@
+// 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_skia.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/gtk_util.h"
+#include "gfx/platform_font_gtk.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;
+
+// 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);
+}
+
+// Pass a width > 0 to force wrapping and elliding.
+static void SetupPangoLayout(PangoLayout* layout,
+ const string16& 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 & gfx::Canvas::NO_ELLIPSIS) {
+ pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_NONE);
+ } else {
+ pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END);
+ }
+
+ if (flags & gfx::Canvas::TEXT_ALIGN_CENTER) {
+ pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
+ } else if (flags & gfx::Canvas::TEXT_ALIGN_RIGHT) {
+ pango_layout_set_alignment(layout, PANGO_ALIGN_RIGHT);
+ }
+
+ if (flags & gfx::Canvas::MULTI_LINE) {
+ pango_layout_set_wrap(layout,
+ (flags & gfx::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 = gfx::GetPangoResolution();
+ if (resolution > 0) {
+ pango_cairo_context_set_resolution(pango_layout_get_context(layout),
+ resolution);
+ }
+
+ PangoFontDescription* desc = font.GetNativeFont();
+ pango_layout_set_font_description(layout, desc);
+ pango_font_description_free(desc);
+
+ // Set text and accelerator character if needed.
+ std::string utf8 = UTF16ToUTF8(text);
+ if (flags & gfx::Canvas::SHOW_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 if (flags & gfx::Canvas::HIDE_PREFIX) {
+ // Remove the ampersand character.
+ utf8 = gfx::RemoveWindowsStyleAccelerators(utf8);
+ pango_layout_set_text(layout, utf8.data(), utf8.size());
+ } else {
+ pango_layout_set_text(layout, utf8.data(), utf8.size());
+ }
+}
+
+// A class to encapsulate string drawing params and operations.
+class DrawStringContext {
+ public:
+ DrawStringContext(gfx::CanvasSkia* canvas,
+ const string16& text,
+ const gfx::Font& font,
+ const gfx::Rect& bounds,
+ const gfx::Rect& clip,
+ int flags);
+ ~DrawStringContext();
+
+ void Draw(const SkColor& text_color);
+ void DrawWithHalo(const SkColor& text_color,
+ const SkColor& halo_color);
+
+ private:
+ const gfx::Rect& bounds_;
+ int flags_;
+ const gfx::Font& font_;
+
+ gfx::CanvasSkia* canvas_;
+ cairo_t* cr_;
+ PangoLayout* layout_;
+
+ int text_x_;
+ int text_y_;
+ int text_width_;
+ int text_height_;
+
+ DISALLOW_COPY_AND_ASSIGN(DrawStringContext);
+};
+
+DrawStringContext::DrawStringContext(gfx::CanvasSkia* canvas,
+ const string16& text,
+ const gfx::Font& font,
+ const gfx::Rect& bounds,
+ const gfx::Rect& clip,
+ int flags)
+ : bounds_(bounds),
+ flags_(flags),
+ font_(font),
+ canvas_(canvas),
+ cr_(NULL),
+ layout_(NULL),
+ text_x_(bounds.x()),
+ text_y_(bounds.y()),
+ text_width_(0),
+ text_height_(0) {
+ DCHECK(!bounds_.IsEmpty());
+
+ cr_ = canvas_->beginPlatformPaint();
+ layout_ = pango_cairo_create_layout(cr_);
+
+ SetupPangoLayout(layout_, text, font, bounds_.width(), flags_);
+
+ pango_layout_set_height(layout_, bounds_.height() * PANGO_SCALE);
+
+ cairo_save(cr_);
+
+ cairo_rectangle(cr_, clip.x(), clip.y(), clip.width(), clip.height());
+ cairo_clip(cr_);
+
+ pango_layout_get_pixel_size(layout_, &text_width_, &text_height_);
+
+ if (flags_ & gfx::Canvas::TEXT_VALIGN_TOP) {
+ // Cairo should draw from the top left corner already.
+ } else if (flags_ & gfx::Canvas::TEXT_VALIGN_BOTTOM) {
+ text_y_ += (bounds.height() - text_height_);
+ } else {
+ // Vertically centered.
+ text_y_ += ((bounds.height() - text_height_) / 2);
+ }
+}
+
+DrawStringContext::~DrawStringContext() {
+ if (font_.GetStyle() & gfx::Font::UNDERLINED) {
+ gfx::PlatformFontGtk* platform_font =
+ static_cast<gfx::PlatformFontGtk*>(font_.platform_font());
+ double underline_y =
+ static_cast<double>(text_y_) + text_height_ +
+ platform_font->underline_position();
+ cairo_set_line_width(cr_, platform_font->underline_thickness());
+ cairo_move_to(cr_, text_x_, underline_y);
+ cairo_line_to(cr_, text_x_ + text_width_, underline_y);
+ cairo_stroke(cr_);
+ }
+ cairo_restore(cr_);
+
+ g_object_unref(layout_);
+ // NOTE: beginPlatformPaint returned its surface, we shouldn't destroy it.
+}
+
+void DrawStringContext::Draw(const SkColor& text_color) {
+ cairo_set_source_rgba(cr_,
+ SkColorGetR(text_color) / 255.0,
+ SkColorGetG(text_color) / 255.0,
+ SkColorGetB(text_color) / 255.0,
+ SkColorGetA(text_color) / 255.0);
+ cairo_move_to(cr_, text_x_, text_y_);
+ pango_cairo_show_layout(cr_, layout_);
+}
+
+void DrawStringContext::DrawWithHalo(const SkColor& text_color,
+ const SkColor& halo_color) {
+ gfx::CanvasSkia text_canvas(bounds_.width() + 2, bounds_.height() + 2, false);
+ text_canvas.FillRectInt(static_cast<SkColor>(0),
+ 0, 0, bounds_.width() + 2, bounds_.height() + 2);
+
+ cairo_t* text_cr = text_canvas.beginPlatformPaint();
+
+ cairo_move_to(text_cr, 1, 1);
+ pango_cairo_layout_path(text_cr, layout_);
+
+ cairo_set_source_rgba(text_cr,
+ SkColorGetR(halo_color) / 255.0,
+ SkColorGetG(halo_color) / 255.0,
+ SkColorGetB(halo_color) / 255.0,
+ SkColorGetA(halo_color) / 255.0);
+ cairo_set_line_width(text_cr, 2.0);
+ cairo_set_line_join(text_cr, CAIRO_LINE_JOIN_ROUND);
+ cairo_stroke_preserve(text_cr);
+
+ cairo_set_operator(text_cr, CAIRO_OPERATOR_SOURCE);
+ cairo_set_source_rgba(text_cr,
+ SkColorGetR(text_color) / 255.0,
+ SkColorGetG(text_color) / 255.0,
+ SkColorGetB(text_color) / 255.0,
+ SkColorGetA(text_color) / 255.0);
+ cairo_fill(text_cr);
+
+ text_canvas.endPlatformPaint();
+
+ const SkBitmap& text_bitmap = const_cast<SkBitmap&>(
+ text_canvas.getTopPlatformDevice().accessBitmap(false));
+ canvas_->DrawBitmapInt(text_bitmap, text_x_ - 1, text_y_ - 1);
+}
+
+} // namespace
+
+namespace gfx {
+
+CanvasSkia::CanvasSkia(int width, int height, bool is_opaque)
+ : skia::PlatformCanvas(width, height, is_opaque) {
+}
+
+CanvasSkia::CanvasSkia() : skia::PlatformCanvas() {
+}
+
+CanvasSkia::~CanvasSkia() {
+}
+
+// static
+void CanvasSkia::SizeStringInt(const string16& 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 CanvasSkia::DrawStringWithHalo(const string16& text,
+ const gfx::Font& font,
+ const SkColor& text_color,
+ const SkColor& halo_color,
+ int x, int y, int w, int h,
+ int flags) {
+ if (w <= 0 || h <= 0)
+ return;
+
+ gfx::Rect bounds(x, y, w, h);
+ gfx::Rect clip(x - 1, y - 1, w + 2, h + 2); // Bigger clip for halo
+ DrawStringContext context(this, text, font, bounds, clip,flags);
+ context.DrawWithHalo(text_color, halo_color);
+}
+
+void CanvasSkia::DrawStringInt(const string16& text,
+ const gfx::Font& font,
+ const SkColor& color,
+ int x, int y, int w, int h,
+ int flags) {
+ if (w <= 0 || h <= 0)
+ return;
+
+ gfx::Rect bounds(x, y, w, h);
+ DrawStringContext context(this, text, font, bounds, bounds, flags);
+ context.Draw(color);
+}
+
+void CanvasSkia::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/ui/gfx/canvas_skia_mac.mm b/ui/gfx/canvas_skia_mac.mm
new file mode 100644
index 0000000..fb30055
--- /dev/null
+++ b/ui/gfx/canvas_skia_mac.mm
@@ -0,0 +1,88 @@
+// 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.
+
+#import <Cocoa/Cocoa.h>
+
+#include "gfx/canvas_skia.h"
+
+#include "base/mac/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 {
+
+CanvasSkia::CanvasSkia(int width, int height, bool is_opaque)
+ : skia::PlatformCanvas(width, height, is_opaque) {
+}
+
+CanvasSkia::CanvasSkia() : skia::PlatformCanvas() {
+}
+
+CanvasSkia::~CanvasSkia() {
+}
+
+// static
+void CanvasSkia::SizeStringInt(const string16& text,
+ const gfx::Font& font,
+ int* width, int* height,
+ int flags) {
+ NSFont* native_font = font.GetNativeFont();
+ NSString* ns_string = base::SysUTF16ToNSString(text);
+ NSDictionary* attributes =
+ [NSDictionary dictionaryWithObject:native_font
+ forKey:NSFontAttributeName];
+ NSSize string_size = [ns_string sizeWithAttributes:attributes];
+ *width = string_size.width;
+ *height = font.GetHeight();
+}
+
+void CanvasSkia::DrawStringInt(const string16& 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:SkColorGetA(color) / 255.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.GetNativeFont(), NSFontAttributeName,
+ ns_color, NSForegroundColorAttributeName,
+ ns_style, NSParagraphStyleAttributeName,
+ nil];
+
+ NSAttributedString* ns_string =
+ [[[NSAttributedString alloc] initWithString:base::SysUTF16ToNSString(text)
+ attributes:attributes] autorelease];
+ base::mac::ScopedCFTypeRef<CTFramesetterRef> framesetter(
+ CTFramesetterCreateWithAttributedString(
+ reinterpret_cast<CFAttributedStringRef>(ns_string)));
+
+ CGRect text_bounds = CGRectMake(x, y, w, h);
+ CGMutablePathRef path = CGPathCreateMutable();
+ CGPathAddRect(path, NULL, text_bounds);
+
+ base::mac::ScopedCFTypeRef<CTFrameRef> frame(
+ CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, NULL));
+ CTFrameDraw(frame, context);
+ CGContextRestoreGState(context);
+ endPlatformPaint();
+}
+
+} // namespace gfx
diff --git a/ui/gfx/canvas_skia_paint.h b/ui/gfx/canvas_skia_paint.h
new file mode 100644
index 0000000..6ad854a
--- /dev/null
+++ b/ui/gfx/canvas_skia_paint.h
@@ -0,0 +1,21 @@
+// 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 UI_GFX_CANVAS_SKIA_PAINT_H_
+#define UI_GFX_CANVAS_SKIA_PAINT_H_
+#pragma once
+
+#include "gfx/canvas_skia.h"
+#include "skia/ext/canvas_paint.h"
+
+// Define a gfx::CanvasSkiaPaint type that wraps our gfx::Canvas like the
+// skia::PlatformCanvasPaint wraps PlatformCanvas.
+
+namespace gfx {
+
+typedef skia::CanvasPaintT<CanvasSkia> CanvasSkiaPaint;
+
+} // namespace gfx
+
+#endif // UI_GFX_CANVAS_SKIA_PAINT_H_
diff --git a/ui/gfx/canvas_skia_win.cc b/ui/gfx/canvas_skia_win.cc
new file mode 100644
index 0000000..03186fa
--- /dev/null
+++ b/ui/gfx/canvas_skia_win.cc
@@ -0,0 +1,301 @@
+// 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_skia.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 string16& text,
+ RECT* text_bounds,
+ int flags) {
+ // Only adjust string directionality if both of the following are true:
+ // 1. The current locale is RTL.
+ // 2. The string itself has RTL directionality.
+ const wchar_t* string_ptr = text.c_str();
+ int string_size = static_cast<int>(text.length());
+
+ string16 localized_text;
+ if (flags & DT_RTLREADING) {
+ localized_text = text;
+ base::i18n::AdjustStringForLocaleDirection(&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 string16& 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::CanvasSkia::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 {
+
+CanvasSkia::CanvasSkia(int width, int height, bool is_opaque)
+ : skia::PlatformCanvas(width, height, is_opaque) {
+}
+
+CanvasSkia::CanvasSkia() : skia::PlatformCanvas() {
+}
+
+CanvasSkia::~CanvasSkia() {
+}
+
+// static
+void CanvasSkia::SizeStringInt(const string16& 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 - 1; // So the trailing \0 fits in 2K.
+ string16 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.GetNativeFont()));
+ 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 CanvasSkia::DrawStringInt(const string16& text,
+ HFONT font,
+ const SkColor& color,
+ int x, int y, int w, int h,
+ int flags) {
+ if (!IntersectsClipRectInt(x, y, w, h))
+ return;
+
+ // Clamp the max amount of text we'll draw to 32K. There seem to be bugs in
+ // DrawText() if you e.g. ask it to character-break a no-whitespace string of
+ // length > 43680 (for which it draws nothing), and since we clamped to 2K in
+ // SizeStringInt() we're unlikely to be able to display this much anyway.
+ 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);
+ COLORREF brush_color = RGB(SkColorGetR(color), SkColorGetG(color),
+ SkColorGetB(color));
+ SetTextColor(dc, brush_color);
+
+ int f = ComputeFormatFlags(flags, clamped_string);
+ DoDrawText(dc, clamped_string, &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 CanvasSkia::DrawStringInt(const string16& text,
+ const gfx::Font& font,
+ const SkColor& color,
+ int x, int y, int w, int h,
+ int flags) {
+ DrawStringInt(text, font.GetNativeFont(), 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 CanvasSkia::DrawStringWithHalo(const string16& 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.
+ CanvasSkia text_canvas(w + 2, h + 2, true);
+ SkPaint bkgnd_paint;
+ bkgnd_paint.setColor(halo_color);
+ text_canvas.DrawRectInt(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.
+ DrawBitmapInt(text_bitmap, x - 1, y - 1);
+}
+
+} // namespace gfx
diff --git a/ui/gfx/codec/DEPS b/ui/gfx/codec/DEPS
new file mode 100644
index 0000000..e4907a6c
--- /dev/null
+++ b/ui/gfx/codec/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+ "+skia",
+ "+third_party/libjpeg",
+ "+third_party/libpng",
+]
diff --git a/ui/gfx/codec/jpeg_codec.cc b/ui/gfx/codec/jpeg_codec.cc
new file mode 100644
index 0000000..ad499e5
--- /dev/null
+++ b/ui/gfx/codec/jpeg_codec.cc
@@ -0,0 +1,535 @@
+// 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/codec/jpeg_codec.h"
+
+#include <setjmp.h>
+
+#include "base/logging.h"
+#include "base/scoped_ptr.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkColorPriv.h"
+
+extern "C" {
+#if defined(USE_SYSTEM_LIBJPEG)
+#include <jpeglib.h>
+#else
+#include "jpeglib.h"
+#endif
+}
+
+namespace gfx {
+
+// Encoder/decoder shared stuff ------------------------------------------------
+
+namespace {
+
+// used to pass error info through the JPEG library
+struct CoderErrorMgr {
+ jpeg_error_mgr pub;
+ jmp_buf setjmp_buffer;
+};
+
+void ErrorExit(jpeg_common_struct* cinfo) {
+ CoderErrorMgr *err = reinterpret_cast<CoderErrorMgr*>(cinfo->err);
+
+ // Return control to the setjmp point.
+ longjmp(err->setjmp_buffer, false);
+}
+
+} // namespace
+
+// Encoder ---------------------------------------------------------------------
+//
+// This code is based on nsJPEGEncoder from Mozilla.
+// Copyright 2005 Google Inc. (Brett Wilson, contributor)
+
+namespace {
+
+// Initial size for the output buffer in the JpegEncoderState below.
+static const int initial_output_buffer_size = 8192;
+
+struct JpegEncoderState {
+ explicit JpegEncoderState(std::vector<unsigned char>* o)
+ : out(o),
+ image_buffer_used(0) {
+ }
+
+ // Output buffer, of which 'image_buffer_used' bytes are actually used (this
+ // will often be less than the actual size of the vector because we size it
+ // so that libjpeg can write directly into it.
+ std::vector<unsigned char>* out;
+
+ // Number of bytes in the 'out' buffer that are actually used (see above).
+ size_t image_buffer_used;
+};
+
+// Initializes the JpegEncoderState for encoding, and tells libjpeg about where
+// the output buffer is.
+//
+// From the JPEG library:
+// "Initialize destination. This is called by jpeg_start_compress() before
+// any data is actually written. It must initialize next_output_byte and
+// free_in_buffer. free_in_buffer must be initialized to a positive value."
+void InitDestination(jpeg_compress_struct* cinfo) {
+ JpegEncoderState* state = static_cast<JpegEncoderState*>(cinfo->client_data);
+ DCHECK(state->image_buffer_used == 0) << "initializing after use";
+
+ state->out->resize(initial_output_buffer_size);
+ state->image_buffer_used = 0;
+
+ cinfo->dest->next_output_byte = &(*state->out)[0];
+ cinfo->dest->free_in_buffer = initial_output_buffer_size;
+}
+
+// Resize the buffer that we give to libjpeg and update our and its state.
+//
+// From the JPEG library:
+// "Callback used by libjpeg whenever the buffer has filled (free_in_buffer
+// reaches zero). In typical applications, it should write out the *entire*
+// buffer (use the saved start address and buffer length; ignore the current
+// state of next_output_byte and free_in_buffer). Then reset the pointer &
+// count to the start of the buffer, and return TRUE indicating that the
+// buffer has been dumped. free_in_buffer must be set to a positive value
+// when TRUE is returned. A FALSE return should only be used when I/O
+// suspension is desired (this operating mode is discussed in the next
+// section)."
+boolean EmptyOutputBuffer(jpeg_compress_struct* cinfo) {
+ JpegEncoderState* state = static_cast<JpegEncoderState*>(cinfo->client_data);
+
+ // note the new size, the buffer is full
+ state->image_buffer_used = state->out->size();
+
+ // expand buffer, just double size each time
+ state->out->resize(state->out->size() * 2);
+
+ // tell libjpeg where to write the next data
+ cinfo->dest->next_output_byte = &(*state->out)[state->image_buffer_used];
+ cinfo->dest->free_in_buffer = state->out->size() - state->image_buffer_used;
+ return 1;
+}
+
+// Cleans up the JpegEncoderState to prepare for returning in the final form.
+//
+// From the JPEG library:
+// "Terminate destination --- called by jpeg_finish_compress() after all data
+// has been written. In most applications, this must flush any data
+// remaining in the buffer. Use either next_output_byte or free_in_buffer to
+// determine how much data is in the buffer."
+void TermDestination(jpeg_compress_struct* cinfo) {
+ JpegEncoderState* state = static_cast<JpegEncoderState*>(cinfo->client_data);
+ DCHECK(state->out->size() >= state->image_buffer_used);
+
+ // update the used byte based on the next byte libjpeg would write to
+ state->image_buffer_used = cinfo->dest->next_output_byte - &(*state->out)[0];
+ DCHECK(state->image_buffer_used < state->out->size()) <<
+ "JPEG library busted, got a bad image buffer size";
+
+ // update our buffer so that it exactly encompases the desired data
+ state->out->resize(state->image_buffer_used);
+}
+
+// Converts RGBA to RGB (removing the alpha values) to prepare to send data to
+// libjpeg. This converts one row of data in rgba with the given width in
+// pixels the the given rgb destination buffer (which should have enough space
+// reserved for the final data).
+void StripAlpha(const unsigned char* rgba, int pixel_width, unsigned char* rgb)
+{
+ for (int x = 0; x < pixel_width; x++) {
+ const unsigned char* pixel_in = &rgba[x * 4];
+ unsigned char* pixel_out = &rgb[x * 3];
+ pixel_out[0] = pixel_in[0];
+ pixel_out[1] = pixel_in[1];
+ pixel_out[2] = pixel_in[2];
+ }
+}
+
+// Converts BGRA to RGB by reordering the color components and dropping the
+// alpha. This converts one row of data in rgba with the given width in
+// pixels the the given rgb destination buffer (which should have enough space
+// reserved for the final data).
+void BGRAtoRGB(const unsigned char* bgra, int pixel_width, unsigned char* rgb)
+{
+ for (int x = 0; x < pixel_width; x++) {
+ const unsigned char* pixel_in = &bgra[x * 4];
+ unsigned char* pixel_out = &rgb[x * 3];
+ pixel_out[0] = pixel_in[2];
+ pixel_out[1] = pixel_in[1];
+ pixel_out[2] = pixel_in[0];
+ }
+}
+
+// This class destroys the given jpeg_compress object when it goes out of
+// scope. It simplifies the error handling in Encode (and even applies to the
+// success case).
+class CompressDestroyer {
+ public:
+ CompressDestroyer() : cinfo_(NULL) {
+ }
+ ~CompressDestroyer() {
+ DestroyManagedObject();
+ }
+ void SetManagedObject(jpeg_compress_struct* ci) {
+ DestroyManagedObject();
+ cinfo_ = ci;
+ }
+ void DestroyManagedObject() {
+ if (cinfo_) {
+ jpeg_destroy_compress(cinfo_);
+ cinfo_ = NULL;
+ }
+ }
+ private:
+ jpeg_compress_struct* cinfo_;
+};
+
+} // namespace
+
+bool JPEGCodec::Encode(const unsigned char* input, ColorFormat format,
+ int w, int h, int row_byte_width,
+ int quality, std::vector<unsigned char>* output) {
+ jpeg_compress_struct cinfo;
+ CompressDestroyer destroyer;
+ destroyer.SetManagedObject(&cinfo);
+ output->clear();
+
+ // We set up the normal JPEG error routines, then override error_exit.
+ // This must be done before the call to create_compress.
+ CoderErrorMgr errmgr;
+ cinfo.err = jpeg_std_error(&errmgr.pub);
+ errmgr.pub.error_exit = ErrorExit;
+ // Establish the setjmp return context for ErrorExit to use.
+ if (setjmp(errmgr.setjmp_buffer)) {
+ // If we get here, the JPEG code has signaled an error.
+ // MSDN notes: "if you intend your code to be portable, do not rely on
+ // correct destruction of frame-based objects when executing a nonlocal
+ // goto using a call to longjmp." So we delete the CompressDestroyer's
+ // object manually instead.
+ destroyer.DestroyManagedObject();
+ return false;
+ }
+
+ // The destroyer will destroy() cinfo on exit.
+ jpeg_create_compress(&cinfo);
+
+ cinfo.image_width = w;
+ cinfo.image_height = h;
+ cinfo.input_components = 3;
+ cinfo.in_color_space = JCS_RGB;
+ cinfo.data_precision = 8;
+
+ jpeg_set_defaults(&cinfo);
+ jpeg_set_quality(&cinfo, quality, 1); // quality here is 0-100
+
+ // set up the destination manager
+ jpeg_destination_mgr destmgr;
+ destmgr.init_destination = InitDestination;
+ destmgr.empty_output_buffer = EmptyOutputBuffer;
+ destmgr.term_destination = TermDestination;
+ cinfo.dest = &destmgr;
+
+ JpegEncoderState state(output);
+ cinfo.client_data = &state;
+
+ jpeg_start_compress(&cinfo, 1);
+
+ // feed it the rows, doing necessary conversions for the color format
+ if (format == FORMAT_RGB) {
+ // no conversion necessary
+ while (cinfo.next_scanline < cinfo.image_height) {
+ const unsigned char* row = &input[cinfo.next_scanline * row_byte_width];
+ jpeg_write_scanlines(&cinfo, const_cast<unsigned char**>(&row), 1);
+ }
+ } else {
+ // get the correct format converter
+ void (*converter)(const unsigned char* in, int w, unsigned char* rgb);
+ if (format == FORMAT_RGBA ||
+ (format == FORMAT_SkBitmap && SK_R32_SHIFT == 0)) {
+ converter = StripAlpha;
+ } else if (format == FORMAT_BGRA ||
+ (format == FORMAT_SkBitmap && SK_B32_SHIFT == 0)) {
+ converter = BGRAtoRGB;
+ } else {
+ NOTREACHED() << "Invalid pixel format";
+ return false;
+ }
+
+ // output row after converting
+ unsigned char* row = new unsigned char[w * 3];
+
+ while (cinfo.next_scanline < cinfo.image_height) {
+ converter(&input[cinfo.next_scanline * row_byte_width], w, row);
+ jpeg_write_scanlines(&cinfo, &row, 1);
+ }
+ delete[] row;
+ }
+
+ jpeg_finish_compress(&cinfo);
+ return true;
+}
+
+// Decoder --------------------------------------------------------------------
+
+namespace {
+
+struct JpegDecoderState {
+ JpegDecoderState(const unsigned char* in, size_t len)
+ : input_buffer(in), input_buffer_length(len) {
+ }
+
+ const unsigned char* input_buffer;
+ size_t input_buffer_length;
+};
+
+// Callback to initialize the source.
+//
+// From the JPEG library:
+// "Initialize source. This is called by jpeg_read_header() before any data is
+// actually read. May leave bytes_in_buffer set to 0 (in which case a
+// fill_input_buffer() call will occur immediately)."
+void InitSource(j_decompress_ptr cinfo) {
+ JpegDecoderState* state = static_cast<JpegDecoderState*>(cinfo->client_data);
+ cinfo->src->next_input_byte = state->input_buffer;
+ cinfo->src->bytes_in_buffer = state->input_buffer_length;
+}
+
+// Callback to fill the buffer. Since our buffer already contains all the data,
+// we should never need to provide more data. If libjpeg thinks it needs more
+// data, our input is probably corrupt.
+//
+// From the JPEG library:
+// "This is called whenever bytes_in_buffer has reached zero and more data is
+// wanted. In typical applications, it should read fresh data into the buffer
+// (ignoring the current state of next_input_byte and bytes_in_buffer), reset
+// the pointer & count to the start of the buffer, and return TRUE indicating
+// that the buffer has been reloaded. It is not necessary to fill the buffer
+// entirely, only to obtain at least one more byte. bytes_in_buffer MUST be
+// set to a positive value if TRUE is returned. A FALSE return should only
+// be used when I/O suspension is desired."
+boolean FillInputBuffer(j_decompress_ptr cinfo) {
+ return false;
+}
+
+// Skip data in the buffer. Since we have all the data at once, this operation
+// is easy. It is not clear if this ever gets called because the JPEG library
+// should be able to do the skip itself (it has all the data).
+//
+// From the JPEG library:
+// "Skip num_bytes worth of data. The buffer pointer and count should be
+// advanced over num_bytes input bytes, refilling the buffer as needed. This
+// is used to skip over a potentially large amount of uninteresting data
+// (such as an APPn marker). In some applications it may be possible to
+// optimize away the reading of the skipped data, but it's not clear that
+// being smart is worth much trouble; large skips are uncommon.
+// bytes_in_buffer may be zero on return. A zero or negative skip count
+// should be treated as a no-op."
+void SkipInputData(j_decompress_ptr cinfo, long num_bytes) {
+ if (num_bytes > static_cast<long>(cinfo->src->bytes_in_buffer)) {
+ // Since all our data should be in the buffer, trying to skip beyond it
+ // means that there is some kind of error or corrupt input data. A 0 for
+ // bytes left means it will call FillInputBuffer which will then fail.
+ cinfo->src->next_input_byte += cinfo->src->bytes_in_buffer;
+ cinfo->src->bytes_in_buffer = 0;
+ } else if (num_bytes > 0) {
+ cinfo->src->bytes_in_buffer -= static_cast<size_t>(num_bytes);
+ cinfo->src->next_input_byte += num_bytes;
+ }
+}
+
+// Our source doesn't need any cleanup, so this is a NOP.
+//
+// From the JPEG library:
+// "Terminate source --- called by jpeg_finish_decompress() after all data has
+// been read to clean up JPEG source manager. NOT called by jpeg_abort() or
+// jpeg_destroy()."
+void TermSource(j_decompress_ptr cinfo) {
+}
+
+// Converts one row of rgb data to rgba data by adding a fully-opaque alpha
+// value.
+void AddAlpha(const unsigned char* rgb, int pixel_width, unsigned char* rgba) {
+ for (int x = 0; x < pixel_width; x++) {
+ const unsigned char* pixel_in = &rgb[x * 3];
+ unsigned char* pixel_out = &rgba[x * 4];
+ pixel_out[0] = pixel_in[0];
+ pixel_out[1] = pixel_in[1];
+ pixel_out[2] = pixel_in[2];
+ pixel_out[3] = 0xff;
+ }
+}
+
+// Converts one row of RGB data to BGRA by reordering the color components and
+// adding alpha values of 0xff.
+void RGBtoBGRA(const unsigned char* bgra, int pixel_width, unsigned char* rgb)
+{
+ for (int x = 0; x < pixel_width; x++) {
+ const unsigned char* pixel_in = &bgra[x * 3];
+ unsigned char* pixel_out = &rgb[x * 4];
+ pixel_out[0] = pixel_in[2];
+ pixel_out[1] = pixel_in[1];
+ pixel_out[2] = pixel_in[0];
+ pixel_out[3] = 0xff;
+ }
+}
+
+// This class destroys the given jpeg_decompress object when it goes out of
+// scope. It simplifies the error handling in Decode (and even applies to the
+// success case).
+class DecompressDestroyer {
+ public:
+ DecompressDestroyer() : cinfo_(NULL) {
+ }
+ ~DecompressDestroyer() {
+ DestroyManagedObject();
+ }
+ void SetManagedObject(jpeg_decompress_struct* ci) {
+ DestroyManagedObject();
+ cinfo_ = ci;
+ }
+ void DestroyManagedObject() {
+ if (cinfo_) {
+ jpeg_destroy_decompress(cinfo_);
+ cinfo_ = NULL;
+ }
+ }
+ private:
+ jpeg_decompress_struct* cinfo_;
+};
+
+} // namespace
+
+bool JPEGCodec::Decode(const unsigned char* input, size_t input_size,
+ ColorFormat format, std::vector<unsigned char>* output,
+ int* w, int* h) {
+ jpeg_decompress_struct cinfo;
+ DecompressDestroyer destroyer;
+ destroyer.SetManagedObject(&cinfo);
+ output->clear();
+
+ // We set up the normal JPEG error routines, then override error_exit.
+ // This must be done before the call to create_decompress.
+ CoderErrorMgr errmgr;
+ cinfo.err = jpeg_std_error(&errmgr.pub);
+ errmgr.pub.error_exit = ErrorExit;
+ // Establish the setjmp return context for ErrorExit to use.
+ if (setjmp(errmgr.setjmp_buffer)) {
+ // If we get here, the JPEG code has signaled an error.
+ // See note in JPEGCodec::Encode() for why we need to destroy the cinfo
+ // manually here.
+ destroyer.DestroyManagedObject();
+ return false;
+ }
+
+ // The destroyer will destroy() cinfo on exit. We don't want to set the
+ // destroyer's object until cinfo is initialized.
+ jpeg_create_decompress(&cinfo);
+
+ // set up the source manager
+ jpeg_source_mgr srcmgr;
+ srcmgr.init_source = InitSource;
+ srcmgr.fill_input_buffer = FillInputBuffer;
+ srcmgr.skip_input_data = SkipInputData;
+ srcmgr.resync_to_restart = jpeg_resync_to_restart; // use default routine
+ srcmgr.term_source = TermSource;
+ cinfo.src = &srcmgr;
+
+ JpegDecoderState state(input, input_size);
+ cinfo.client_data = &state;
+
+ // fill the file metadata into our buffer
+ if (jpeg_read_header(&cinfo, true) != JPEG_HEADER_OK)
+ return false;
+
+ // we want to always get RGB data out
+ switch (cinfo.jpeg_color_space) {
+ case JCS_GRAYSCALE:
+ case JCS_RGB:
+ case JCS_YCbCr:
+ cinfo.out_color_space = JCS_RGB;
+ break;
+ case JCS_CMYK:
+ case JCS_YCCK:
+ default:
+ // Mozilla errors out on these color spaces, so I presume that the jpeg
+ // library can't do automatic color space conversion for them. We don't
+ // care about these anyway.
+ return false;
+ }
+ cinfo.output_components = 3;
+
+ jpeg_calc_output_dimensions(&cinfo);
+ *w = cinfo.output_width;
+ *h = cinfo.output_height;
+
+ jpeg_start_decompress(&cinfo);
+
+ // FIXME(brettw) we may want to allow the capability for callers to request
+ // how to align row lengths as we do for the compressor.
+ int row_read_stride = cinfo.output_width * cinfo.output_components;
+
+ if (format == FORMAT_RGB) {
+ // easy case, row needs no conversion
+ int row_write_stride = row_read_stride;
+ output->resize(row_write_stride * cinfo.output_height);
+
+ for (int row = 0; row < static_cast<int>(cinfo.output_height); row++) {
+ unsigned char* rowptr = &(*output)[row * row_write_stride];
+ if (!jpeg_read_scanlines(&cinfo, &rowptr, 1))
+ return false;
+ }
+ } else {
+ // Rows need conversion to output format: read into a temporary buffer and
+ // expand to the final one. Performance: we could avoid the extra
+ // allocation by doing the expansion in-place.
+ int row_write_stride;
+ void (*converter)(const unsigned char* rgb, int w, unsigned char* out);
+ if (format == FORMAT_RGBA ||
+ (format == FORMAT_SkBitmap && SK_R32_SHIFT == 0)) {
+ row_write_stride = cinfo.output_width * 4;
+ converter = AddAlpha;
+ } else if (format == FORMAT_BGRA ||
+ (format == FORMAT_SkBitmap && SK_B32_SHIFT == 0)) {
+ row_write_stride = cinfo.output_width * 4;
+ converter = RGBtoBGRA;
+ } else {
+ NOTREACHED() << "Invalid pixel format";
+ jpeg_destroy_decompress(&cinfo);
+ return false;
+ }
+
+ output->resize(row_write_stride * cinfo.output_height);
+
+ scoped_array<unsigned char> row_data(new unsigned char[row_read_stride]);
+ unsigned char* rowptr = row_data.get();
+ for (int row = 0; row < static_cast<int>(cinfo.output_height); row++) {
+ if (!jpeg_read_scanlines(&cinfo, &rowptr, 1))
+ return false;
+ converter(rowptr, *w, &(*output)[row * row_write_stride]);
+ }
+ }
+
+ jpeg_finish_decompress(&cinfo);
+ jpeg_destroy_decompress(&cinfo);
+ return true;
+}
+
+// static
+SkBitmap* JPEGCodec::Decode(const unsigned char* input, size_t input_size) {
+ int w, h;
+ std::vector<unsigned char> data_vector;
+ if (!Decode(input, input_size, FORMAT_SkBitmap, &data_vector, &w, &h))
+ return NULL;
+
+ // Skia only handles 32 bit images.
+ int data_length = w * h * 4;
+
+ SkBitmap* bitmap = new SkBitmap();
+ bitmap->setConfig(SkBitmap::kARGB_8888_Config, w, h);
+ bitmap->allocPixels();
+ memcpy(bitmap->getAddr32(0, 0), &data_vector[0], data_length);
+
+ return bitmap;
+}
+
+} // namespace gfx
diff --git a/ui/gfx/codec/jpeg_codec.h b/ui/gfx/codec/jpeg_codec.h
new file mode 100644
index 0000000..5ad4a4a8
--- /dev/null
+++ b/ui/gfx/codec/jpeg_codec.h
@@ -0,0 +1,68 @@
+// 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 UI_GFX_CODEC_JPEG_CODEC_H_
+#define UI_GFX_CODEC_JPEG_CODEC_H_
+#pragma once
+
+#include <vector>
+
+class SkBitmap;
+
+namespace gfx {
+
+// Interface for encoding/decoding JPEG data. This is a wrapper around libjpeg,
+// which has an inconvenient interface for callers. This is only used for UI
+// elements, WebKit has its own more complicated JPEG decoder which handles,
+// among other things, partially downloaded data.
+class JPEGCodec {
+ public:
+ enum ColorFormat {
+ // 3 bytes per pixel (packed), in RGB order regardless of endianness.
+ // This is the native JPEG format.
+ FORMAT_RGB,
+
+ // 4 bytes per pixel, in RGBA order in mem regardless of endianness.
+ FORMAT_RGBA,
+
+ // 4 bytes per pixel, in BGRA order in mem regardless of endianness.
+ // This is the default Windows DIB order.
+ FORMAT_BGRA,
+
+ // 4 bytes per pixel, it can be either RGBA or BGRA. It depends on the bit
+ // order in kARGB_8888_Config skia bitmap.
+ FORMAT_SkBitmap
+ };
+
+ // Encodes the given raw 'input' data, with each pixel being represented as
+ // given in 'format'. The encoded JPEG data will be written into the supplied
+ // vector and true will be returned on success. On failure (false), the
+ // contents of the output buffer are undefined.
+ //
+ // w, h: dimensions of the image
+ // row_byte_width: the width in bytes of each row. This may be greater than
+ // w * bytes_per_pixel if there is extra padding at the end of each row
+ // (often, each row is padded to the next machine word).
+ // quality: an integer in the range 0-100, where 100 is the highest quality.
+ static bool Encode(const unsigned char* input, ColorFormat format,
+ int w, int h, int row_byte_width,
+ int quality, std::vector<unsigned char>* output);
+
+ // Decodes the JPEG data contained in input of length input_size. The
+ // decoded data will be placed in *output with the dimensions in *w and *h
+ // on success (returns true). This data will be written in the'format'
+ // format. On failure, the values of these output variables is undefined.
+ static bool Decode(const unsigned char* input, size_t input_size,
+ ColorFormat format, std::vector<unsigned char>* output,
+ int* w, int* h);
+
+ // Decodes the JPEG data contained in input of length input_size. If
+ // successful, a SkBitmap is created and returned. It is up to the caller
+ // to delete the returned bitmap.
+ static SkBitmap* Decode(const unsigned char* input, size_t input_size);
+};
+
+} // namespace gfx
+
+#endif // UI_GFX_CODEC_JPEG_CODEC_H_
diff --git a/ui/gfx/codec/jpeg_codec_unittest.cc b/ui/gfx/codec/jpeg_codec_unittest.cc
new file mode 100644
index 0000000..16fc848
--- /dev/null
+++ b/ui/gfx/codec/jpeg_codec_unittest.cc
@@ -0,0 +1,151 @@
+// 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 <math.h>
+
+#include "gfx/codec/jpeg_codec.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace gfx {
+
+// out of 100, this indicates how compressed it will be, this should be changed
+// with jpeg equality threshold
+// static int jpeg_quality = 75; // FIXME(brettw)
+static int jpeg_quality = 100;
+
+// The threshold of average color differences where we consider two images
+// equal. This number was picked to be a little above the observed difference
+// using the above quality.
+static double jpeg_equality_threshold = 1.0;
+
+// Computes the average difference between each value in a and b. A and b
+// should be the same size. Used to see if two images are approximately equal
+// in the presence of compression.
+static double AveragePixelDelta(const std::vector<unsigned char>& a,
+ const std::vector<unsigned char>& b) {
+ // if the sizes are different, say the average difference is the maximum
+ if (a.size() != b.size())
+ return 255.0;
+ if (a.size() == 0)
+ return 0; // prevent divide by 0 below
+
+ double acc = 0.0;
+ for (size_t i = 0; i < a.size(); i++)
+ acc += fabs(static_cast<double>(a[i]) - static_cast<double>(b[i]));
+
+ return acc / static_cast<double>(a.size());
+}
+
+static void MakeRGBImage(int w, int h, std::vector<unsigned char>* dat) {
+ dat->resize(w * h * 3);
+ for (int y = 0; y < h; y++) {
+ for (int x = 0; x < w; x++) {
+ unsigned char* org_px = &(*dat)[(y * w + x) * 3];
+ org_px[0] = x * 3; // r
+ org_px[1] = x * 3 + 1; // g
+ org_px[2] = x * 3 + 2; // b
+ }
+ }
+}
+
+TEST(JPEGCodec, EncodeDecodeRGB) {
+ int w = 20, h = 20;
+
+ // create an image with known values
+ std::vector<unsigned char> original;
+ MakeRGBImage(w, h, &original);
+
+ // encode, making sure it was compressed some
+ std::vector<unsigned char> encoded;
+ EXPECT_TRUE(JPEGCodec::Encode(&original[0], JPEGCodec::FORMAT_RGB, w, h,
+ w * 3, jpeg_quality, &encoded));
+ EXPECT_GT(original.size(), encoded.size());
+
+ // decode, it should have the same size as the original
+ std::vector<unsigned char> decoded;
+ int outw, outh;
+ EXPECT_TRUE(JPEGCodec::Decode(&encoded[0], encoded.size(),
+ JPEGCodec::FORMAT_RGB, &decoded,
+ &outw, &outh));
+ ASSERT_EQ(w, outw);
+ ASSERT_EQ(h, outh);
+ ASSERT_EQ(original.size(), decoded.size());
+
+ // Images must be approximately equal (compression will have introduced some
+ // minor artifacts).
+ ASSERT_GE(jpeg_equality_threshold, AveragePixelDelta(original, decoded));
+}
+
+TEST(JPEGCodec, EncodeDecodeRGBA) {
+ int w = 20, h = 20;
+
+ // create an image with known values, a must be opaque because it will be
+ // lost during compression
+ std::vector<unsigned char> original;
+ original.resize(w * h * 4);
+ for (int y = 0; y < h; y++) {
+ for (int x = 0; x < w; x++) {
+ unsigned char* org_px = &original[(y * w + x) * 4];
+ org_px[0] = x * 3; // r
+ org_px[1] = x * 3 + 1; // g
+ org_px[2] = x * 3 + 2; // b
+ org_px[3] = 0xFF; // a (opaque)
+ }
+ }
+
+ // encode, making sure it was compressed some
+ std::vector<unsigned char> encoded;
+ EXPECT_TRUE(JPEGCodec::Encode(&original[0], JPEGCodec::FORMAT_RGBA, w, h,
+ w * 4, jpeg_quality, &encoded));
+ EXPECT_GT(original.size(), encoded.size());
+
+ // decode, it should have the same size as the original
+ std::vector<unsigned char> decoded;
+ int outw, outh;
+ EXPECT_TRUE(JPEGCodec::Decode(&encoded[0], encoded.size(),
+ JPEGCodec::FORMAT_RGBA, &decoded,
+ &outw, &outh));
+ ASSERT_EQ(w, outw);
+ ASSERT_EQ(h, outh);
+ ASSERT_EQ(original.size(), decoded.size());
+
+ // Images must be approximately equal (compression will have introduced some
+ // minor artifacts).
+ ASSERT_GE(jpeg_equality_threshold, AveragePixelDelta(original, decoded));
+}
+
+// Test that corrupted data decompression causes failures.
+TEST(JPEGCodec, DecodeCorrupted) {
+ int w = 20, h = 20;
+
+ // some random data (an uncompressed image)
+ std::vector<unsigned char> original;
+ MakeRGBImage(w, h, &original);
+
+ // it should fail when given non-JPEG compressed data
+ std::vector<unsigned char> output;
+ int outw, outh;
+ ASSERT_FALSE(JPEGCodec::Decode(&original[0], original.size(),
+ JPEGCodec::FORMAT_RGB, &output,
+ &outw, &outh));
+
+ // make some compressed data
+ std::vector<unsigned char> compressed;
+ ASSERT_TRUE(JPEGCodec::Encode(&original[0], JPEGCodec::FORMAT_RGB, w, h,
+ w * 3, jpeg_quality, &compressed));
+
+ // try decompressing a truncated version
+ ASSERT_FALSE(JPEGCodec::Decode(&compressed[0], compressed.size() / 2,
+ JPEGCodec::FORMAT_RGB, &output,
+ &outw, &outh));
+
+ // corrupt it and try decompressing that
+ for (int i = 10; i < 30; i++)
+ compressed[i] = i;
+ ASSERT_FALSE(JPEGCodec::Decode(&compressed[0], compressed.size(),
+ JPEGCodec::FORMAT_RGB, &output,
+ &outw, &outh));
+}
+
+} // namespace gfx
diff --git a/ui/gfx/codec/png_codec.cc b/ui/gfx/codec/png_codec.cc
new file mode 100644
index 0000000..5fd6d7a
--- /dev/null
+++ b/ui/gfx/codec/png_codec.cc
@@ -0,0 +1,699 @@
+// 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/codec/png_codec.h"
+
+#include "base/logging.h"
+#include "base/scoped_ptr.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkUnPreMultiply.h"
+#include "third_party/skia/include/core/SkColorPriv.h"
+
+extern "C" {
+#if defined(USE_SYSTEM_LIBPNG)
+#include <png.h>
+#else
+#include "third_party/libpng/png.h"
+#endif
+}
+
+namespace gfx {
+
+namespace {
+
+// Converts BGRA->RGBA and RGBA->BGRA.
+void ConvertBetweenBGRAandRGBA(const unsigned char* input, int pixel_width,
+ unsigned char* output, bool* is_opaque) {
+ for (int x = 0; x < pixel_width; x++) {
+ const unsigned char* pixel_in = &input[x * 4];
+ unsigned char* pixel_out = &output[x * 4];
+ pixel_out[0] = pixel_in[2];
+ pixel_out[1] = pixel_in[1];
+ pixel_out[2] = pixel_in[0];
+ pixel_out[3] = pixel_in[3];
+ }
+}
+
+void ConvertRGBAtoRGB(const unsigned char* rgba, int pixel_width,
+ unsigned char* rgb, bool* is_opaque) {
+ for (int x = 0; x < pixel_width; x++) {
+ const unsigned char* pixel_in = &rgba[x * 4];
+ unsigned char* pixel_out = &rgb[x * 3];
+ pixel_out[0] = pixel_in[0];
+ pixel_out[1] = pixel_in[1];
+ pixel_out[2] = pixel_in[2];
+ }
+}
+
+void ConvertRGBtoSkia(const unsigned char* rgb, int pixel_width,
+ unsigned char* rgba, bool* is_opaque) {
+ for (int x = 0; x < pixel_width; x++) {
+ const unsigned char* pixel_in = &rgb[x * 3];
+ uint32_t* pixel_out = reinterpret_cast<uint32_t*>(&rgba[x * 4]);
+ *pixel_out = SkPackARGB32(0xFF, pixel_in[0], pixel_in[1], pixel_in[2]);
+ }
+}
+
+void ConvertRGBAtoSkia(const unsigned char* rgb, int pixel_width,
+ unsigned char* rgba, bool* is_opaque) {
+ int total_length = pixel_width * 4;
+ for (int x = 0; x < total_length; x += 4) {
+ const unsigned char* pixel_in = &rgb[x];
+ uint32_t* pixel_out = reinterpret_cast<uint32_t*>(&rgba[x]);
+
+ unsigned char alpha = pixel_in[3];
+ if (alpha != 255) {
+ *is_opaque = false;
+ *pixel_out = SkPreMultiplyARGB(alpha,
+ pixel_in[0], pixel_in[1], pixel_in[2]);
+ } else {
+ *pixel_out = SkPackARGB32(alpha,
+ pixel_in[0], pixel_in[1], pixel_in[2]);
+ }
+ }
+}
+
+void ConvertSkiatoRGB(const unsigned char* skia, int pixel_width,
+ unsigned char* rgb, bool* is_opaque) {
+ for (int x = 0; x < pixel_width; x++) {
+ const uint32_t pixel_in = *reinterpret_cast<const uint32_t*>(&skia[x * 4]);
+ unsigned char* pixel_out = &rgb[x * 3];
+
+ int alpha = SkGetPackedA32(pixel_in);
+ if (alpha != 0 && alpha != 255) {
+ SkColor unmultiplied = SkUnPreMultiply::PMColorToColor(pixel_in);
+ pixel_out[0] = SkColorGetR(unmultiplied);
+ pixel_out[1] = SkColorGetG(unmultiplied);
+ pixel_out[2] = SkColorGetB(unmultiplied);
+ } else {
+ pixel_out[0] = SkGetPackedR32(pixel_in);
+ pixel_out[1] = SkGetPackedG32(pixel_in);
+ pixel_out[2] = SkGetPackedB32(pixel_in);
+ }
+ }
+}
+
+void ConvertSkiatoRGBA(const unsigned char* skia, int pixel_width,
+ unsigned char* rgba, bool* is_opaque) {
+ int total_length = pixel_width * 4;
+ for (int i = 0; i < total_length; i += 4) {
+ const uint32_t pixel_in = *reinterpret_cast<const uint32_t*>(&skia[i]);
+
+ // Pack the components here.
+ int alpha = SkGetPackedA32(pixel_in);
+ if (alpha != 0 && alpha != 255) {
+ SkColor unmultiplied = SkUnPreMultiply::PMColorToColor(pixel_in);
+ rgba[i + 0] = SkColorGetR(unmultiplied);
+ rgba[i + 1] = SkColorGetG(unmultiplied);
+ rgba[i + 2] = SkColorGetB(unmultiplied);
+ rgba[i + 3] = alpha;
+ } else {
+ rgba[i + 0] = SkGetPackedR32(pixel_in);
+ rgba[i + 1] = SkGetPackedG32(pixel_in);
+ rgba[i + 2] = SkGetPackedB32(pixel_in);
+ rgba[i + 3] = alpha;
+ }
+ }
+}
+
+} // namespace
+
+// Decoder --------------------------------------------------------------------
+//
+// This code is based on WebKit libpng interface (PNGImageDecoder), which is
+// in turn based on the Mozilla png decoder.
+
+namespace {
+
+// Gamma constants: We assume we're on Windows which uses a gamma of 2.2.
+const double kMaxGamma = 21474.83; // Maximum gamma accepted by png library.
+const double kDefaultGamma = 2.2;
+const double kInverseGamma = 1.0 / kDefaultGamma;
+
+class PngDecoderState {
+ public:
+ // Output is a vector<unsigned char>.
+ PngDecoderState(PNGCodec::ColorFormat ofmt, std::vector<unsigned char>* o)
+ : output_format(ofmt),
+ output_channels(0),
+ bitmap(NULL),
+ is_opaque(true),
+ output(o),
+ row_converter(NULL),
+ width(0),
+ height(0),
+ done(false) {
+ }
+
+ // Output is an SkBitmap.
+ explicit PngDecoderState(SkBitmap* skbitmap)
+ : output_format(PNGCodec::FORMAT_SkBitmap),
+ output_channels(0),
+ bitmap(skbitmap),
+ is_opaque(true),
+ output(NULL),
+ row_converter(NULL),
+ width(0),
+ height(0),
+ done(false) {
+ }
+
+ PNGCodec::ColorFormat output_format;
+ int output_channels;
+
+ // An incoming SkBitmap to write to. If NULL, we write to output instead.
+ SkBitmap* bitmap;
+
+ // Used during the reading of an SkBitmap. Defaults to true until we see a
+ // pixel with anything other than an alpha of 255.
+ bool is_opaque;
+
+ // The other way to decode output, where we write into an intermediary buffer
+ // instead of directly to an SkBitmap.
+ std::vector<unsigned char>* output;
+
+ // Called to convert a row from the library to the correct output format.
+ // When NULL, no conversion is necessary.
+ void (*row_converter)(const unsigned char* in, int w, unsigned char* out,
+ bool* is_opaque);
+
+ // Size of the image, set in the info callback.
+ int width;
+ int height;
+
+ // Set to true when we've found the end of the data.
+ bool done;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PngDecoderState);
+};
+
+void ConvertRGBtoRGBA(const unsigned char* rgb, int pixel_width,
+ unsigned char* rgba, bool* is_opaque) {
+ for (int x = 0; x < pixel_width; x++) {
+ const unsigned char* pixel_in = &rgb[x * 3];
+ unsigned char* pixel_out = &rgba[x * 4];
+ pixel_out[0] = pixel_in[0];
+ pixel_out[1] = pixel_in[1];
+ pixel_out[2] = pixel_in[2];
+ pixel_out[3] = 0xff;
+ }
+}
+
+void ConvertRGBtoBGRA(const unsigned char* rgb, int pixel_width,
+ unsigned char* bgra, bool* is_opaque) {
+ for (int x = 0; x < pixel_width; x++) {
+ const unsigned char* pixel_in = &rgb[x * 3];
+ unsigned char* pixel_out = &bgra[x * 4];
+ pixel_out[0] = pixel_in[2];
+ pixel_out[1] = pixel_in[1];
+ pixel_out[2] = pixel_in[0];
+ pixel_out[3] = 0xff;
+ }
+}
+
+// Called when the png header has been read. This code is based on the WebKit
+// PNGImageDecoder
+void DecodeInfoCallback(png_struct* png_ptr, png_info* info_ptr) {
+ PngDecoderState* state = static_cast<PngDecoderState*>(
+ png_get_progressive_ptr(png_ptr));
+
+ int bit_depth, color_type, interlace_type, compression_type;
+ int filter_type, channels;
+ png_uint_32 w, h;
+ png_get_IHDR(png_ptr, info_ptr, &w, &h, &bit_depth, &color_type,
+ &interlace_type, &compression_type, &filter_type);
+
+ // Bounds check. When the image is unreasonably big, we'll error out and
+ // end up back at the setjmp call when we set up decoding. "Unreasonably big"
+ // means "big enough that w * h * 32bpp might overflow an int"; we choose this
+ // threshold to match WebKit and because a number of places in code assume
+ // that an image's size (in bytes) fits in a (signed) int.
+ unsigned long long total_size =
+ static_cast<unsigned long long>(w) * static_cast<unsigned long long>(h);
+ if (total_size > ((1 << 29) - 1))
+ longjmp(png_jmpbuf(png_ptr), 1);
+ state->width = static_cast<int>(w);
+ state->height = static_cast<int>(h);
+
+ // Expand to ensure we use 24-bit for RGB and 32-bit for RGBA.
+ if (color_type == PNG_COLOR_TYPE_PALETTE ||
+ (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8))
+ png_set_expand(png_ptr);
+
+ // Transparency for paletted images.
+ if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
+ png_set_expand(png_ptr);
+
+ // Convert 16-bit to 8-bit.
+ if (bit_depth == 16)
+ png_set_strip_16(png_ptr);
+
+ // Expand grayscale to RGB.
+ if (color_type == PNG_COLOR_TYPE_GRAY ||
+ color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+ png_set_gray_to_rgb(png_ptr);
+
+ // Deal with gamma and keep it under our control.
+ double gamma;
+ if (png_get_gAMA(png_ptr, info_ptr, &gamma)) {
+ if (gamma <= 0.0 || gamma > kMaxGamma) {
+ gamma = kInverseGamma;
+ png_set_gAMA(png_ptr, info_ptr, gamma);
+ }
+ png_set_gamma(png_ptr, kDefaultGamma, gamma);
+ } else {
+ png_set_gamma(png_ptr, kDefaultGamma, kInverseGamma);
+ }
+
+ // Tell libpng to send us rows for interlaced pngs.
+ if (interlace_type == PNG_INTERLACE_ADAM7)
+ png_set_interlace_handling(png_ptr);
+
+ // Update our info now
+ png_read_update_info(png_ptr, info_ptr);
+ channels = png_get_channels(png_ptr, info_ptr);
+
+ // Pick our row format converter necessary for this data.
+ if (channels == 3) {
+ switch (state->output_format) {
+ case PNGCodec::FORMAT_RGB:
+ state->row_converter = NULL; // no conversion necessary
+ state->output_channels = 3;
+ break;
+ case PNGCodec::FORMAT_RGBA:
+ state->row_converter = &ConvertRGBtoRGBA;
+ state->output_channels = 4;
+ break;
+ case PNGCodec::FORMAT_BGRA:
+ state->row_converter = &ConvertRGBtoBGRA;
+ state->output_channels = 4;
+ break;
+ case PNGCodec::FORMAT_SkBitmap:
+ state->row_converter = &ConvertRGBtoSkia;
+ state->output_channels = 4;
+ break;
+ default:
+ NOTREACHED() << "Unknown output format";
+ break;
+ }
+ } else if (channels == 4) {
+ switch (state->output_format) {
+ case PNGCodec::FORMAT_RGB:
+ state->row_converter = &ConvertRGBAtoRGB;
+ state->output_channels = 3;
+ break;
+ case PNGCodec::FORMAT_RGBA:
+ state->row_converter = NULL; // no conversion necessary
+ state->output_channels = 4;
+ break;
+ case PNGCodec::FORMAT_BGRA:
+ state->row_converter = &ConvertBetweenBGRAandRGBA;
+ state->output_channels = 4;
+ break;
+ case PNGCodec::FORMAT_SkBitmap:
+ state->row_converter = &ConvertRGBAtoSkia;
+ state->output_channels = 4;
+ break;
+ default:
+ NOTREACHED() << "Unknown output format";
+ break;
+ }
+ } else {
+ NOTREACHED() << "Unknown input channels";
+ longjmp(png_jmpbuf(png_ptr), 1);
+ }
+
+ if (state->bitmap) {
+ state->bitmap->setConfig(SkBitmap::kARGB_8888_Config,
+ state->width, state->height);
+ state->bitmap->allocPixels();
+ } else if (state->output) {
+ state->output->resize(
+ state->width * state->output_channels * state->height);
+ }
+}
+
+void DecodeRowCallback(png_struct* png_ptr, png_byte* new_row,
+ png_uint_32 row_num, int pass) {
+ PngDecoderState* state = static_cast<PngDecoderState*>(
+ png_get_progressive_ptr(png_ptr));
+
+ DCHECK(pass == 0) << "We didn't turn on interlace handling, but libpng is "
+ "giving us interlaced data.";
+ if (static_cast<int>(row_num) > state->height) {
+ NOTREACHED() << "Invalid row";
+ return;
+ }
+
+ unsigned char* base = NULL;
+ if (state->bitmap)
+ base = reinterpret_cast<unsigned char*>(state->bitmap->getAddr32(0, 0));
+ else if (state->output)
+ base = &state->output->front();
+
+ unsigned char* dest = &base[state->width * state->output_channels * row_num];
+ if (state->row_converter)
+ state->row_converter(new_row, state->width, dest, &state->is_opaque);
+ else
+ memcpy(dest, new_row, state->width * state->output_channels);
+}
+
+void DecodeEndCallback(png_struct* png_ptr, png_info* info) {
+ PngDecoderState* state = static_cast<PngDecoderState*>(
+ png_get_progressive_ptr(png_ptr));
+
+ // Mark the image as complete, this will tell the Decode function that we
+ // have successfully found the end of the data.
+ state->done = true;
+}
+
+// Automatically destroys the given read structs on destruction to make
+// cleanup and error handling code cleaner.
+class PngReadStructDestroyer {
+ public:
+ PngReadStructDestroyer(png_struct** ps, png_info** pi) : ps_(ps), pi_(pi) {
+ }
+ ~PngReadStructDestroyer() {
+ png_destroy_read_struct(ps_, pi_, NULL);
+ }
+ private:
+ png_struct** ps_;
+ png_info** pi_;
+};
+
+bool BuildPNGStruct(const unsigned char* input, size_t input_size,
+ png_struct** png_ptr, png_info** info_ptr) {
+ if (input_size < 8)
+ return false; // Input data too small to be a png
+
+ // Have libpng check the signature, it likes the first 8 bytes.
+ if (png_sig_cmp(const_cast<unsigned char*>(input), 0, 8) != 0)
+ return false;
+
+ *png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (!*png_ptr)
+ return false;
+
+ *info_ptr = png_create_info_struct(*png_ptr);
+ if (!*info_ptr) {
+ png_destroy_read_struct(png_ptr, NULL, NULL);
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace
+
+// static
+bool PNGCodec::Decode(const unsigned char* input, size_t input_size,
+ ColorFormat format, std::vector<unsigned char>* output,
+ int* w, int* h) {
+ png_struct* png_ptr = NULL;
+ png_info* info_ptr = NULL;
+ if (!BuildPNGStruct(input, input_size, &png_ptr, &info_ptr))
+ return false;
+
+ PngReadStructDestroyer destroyer(&png_ptr, &info_ptr);
+ if (setjmp(png_jmpbuf(png_ptr))) {
+ // The destroyer will ensure that the structures are cleaned up in this
+ // case, even though we may get here as a jump from random parts of the
+ // PNG library called below.
+ return false;
+ }
+
+ PngDecoderState state(format, output);
+
+ png_set_progressive_read_fn(png_ptr, &state, &DecodeInfoCallback,
+ &DecodeRowCallback, &DecodeEndCallback);
+ png_process_data(png_ptr,
+ info_ptr,
+ const_cast<unsigned char*>(input),
+ input_size);
+
+ if (!state.done) {
+ // Fed it all the data but the library didn't think we got all the data, so
+ // this file must be truncated.
+ output->clear();
+ return false;
+ }
+
+ *w = state.width;
+ *h = state.height;
+ return true;
+}
+
+// static
+bool PNGCodec::Decode(const unsigned char* input, size_t input_size,
+ SkBitmap* bitmap) {
+ DCHECK(bitmap);
+ png_struct* png_ptr = NULL;
+ png_info* info_ptr = NULL;
+ if (!BuildPNGStruct(input, input_size, &png_ptr, &info_ptr))
+ return false;
+
+ PngReadStructDestroyer destroyer(&png_ptr, &info_ptr);
+ if (setjmp(png_jmpbuf(png_ptr))) {
+ // The destroyer will ensure that the structures are cleaned up in this
+ // case, even though we may get here as a jump from random parts of the
+ // PNG library called below.
+ return false;
+ }
+
+ PngDecoderState state(bitmap);
+
+ png_set_progressive_read_fn(png_ptr, &state, &DecodeInfoCallback,
+ &DecodeRowCallback, &DecodeEndCallback);
+ png_process_data(png_ptr,
+ info_ptr,
+ const_cast<unsigned char*>(input),
+ input_size);
+
+ if (!state.done) {
+ return false;
+ }
+
+ // Set the bitmap's opaqueness based on what we saw.
+ bitmap->setIsOpaque(state.is_opaque);
+
+ return true;
+}
+
+// static
+SkBitmap* PNGCodec::CreateSkBitmapFromBGRAFormat(
+ std::vector<unsigned char>& bgra, int width, int height) {
+ SkBitmap* bitmap = new SkBitmap();
+ bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);
+ bitmap->allocPixels();
+
+ bool opaque = false;
+ unsigned char* bitmap_data =
+ reinterpret_cast<unsigned char*>(bitmap->getAddr32(0, 0));
+ for (int i = width * height * 4 - 4; i >= 0; i -= 4) {
+ unsigned char alpha = bgra[i + 3];
+ if (!opaque && alpha != 255) {
+ opaque = false;
+ }
+ bitmap_data[i + 3] = alpha;
+ bitmap_data[i] = (bgra[i] * alpha) >> 8;
+ bitmap_data[i + 1] = (bgra[i + 1] * alpha) >> 8;
+ bitmap_data[i + 2] = (bgra[i + 2] * alpha) >> 8;
+ }
+
+ bitmap->setIsOpaque(opaque);
+ return bitmap;
+}
+
+// Encoder --------------------------------------------------------------------
+//
+// This section of the code is based on nsPNGEncoder.cpp in Mozilla
+// (Copyright 2005 Google Inc.)
+
+namespace {
+
+// Passed around as the io_ptr in the png structs so our callbacks know where
+// to write data.
+struct PngEncoderState {
+ explicit PngEncoderState(std::vector<unsigned char>* o) : out(o) {}
+ std::vector<unsigned char>* out;
+};
+
+// Called by libpng to flush its internal buffer to ours.
+void EncoderWriteCallback(png_structp png, png_bytep data, png_size_t size) {
+ PngEncoderState* state = static_cast<PngEncoderState*>(png_get_io_ptr(png));
+ DCHECK(state->out);
+
+ size_t old_size = state->out->size();
+ state->out->resize(old_size + size);
+ memcpy(&(*state->out)[old_size], data, size);
+}
+
+void FakeFlushCallback(png_structp png) {
+ // We don't need to perform any flushing since we aren't doing real IO, but
+ // we're required to provide this function by libpng.
+}
+
+void ConvertBGRAtoRGB(const unsigned char* bgra, int pixel_width,
+ unsigned char* rgb, bool* is_opaque) {
+ for (int x = 0; x < pixel_width; x++) {
+ const unsigned char* pixel_in = &bgra[x * 4];
+ unsigned char* pixel_out = &rgb[x * 3];
+ pixel_out[0] = pixel_in[2];
+ pixel_out[1] = pixel_in[1];
+ pixel_out[2] = pixel_in[0];
+ }
+}
+
+// The type of functions usable for converting between pixel formats.
+typedef void (*FormatConverter)(const unsigned char* in, int w,
+ unsigned char* out, bool* is_opaque);
+
+// libpng uses a wacky setjmp-based API, which makes the compiler nervous.
+// We constrain all of the calls we make to libpng where the setjmp() is in
+// place to this function.
+// Returns true on success.
+bool DoLibpngWrite(png_struct* png_ptr, png_info* info_ptr,
+ PngEncoderState* state,
+ int width, int height, int row_byte_width,
+ const unsigned char* input,
+ int png_output_color_type, int output_color_components,
+ FormatConverter converter) {
+ // Make sure to not declare any locals here -- locals in the presence
+ // of setjmp() in C++ code makes gcc complain.
+
+ if (setjmp(png_jmpbuf(png_ptr)))
+ return false;
+
+ // Set our callback for libpng to give us the data.
+ png_set_write_fn(png_ptr, state, EncoderWriteCallback, FakeFlushCallback);
+
+ png_set_IHDR(png_ptr, info_ptr, width, height, 8, png_output_color_type,
+ PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
+ PNG_FILTER_TYPE_DEFAULT);
+ png_write_info(png_ptr, info_ptr);
+
+ if (!converter) {
+ // No conversion needed, give the data directly to libpng.
+ for (int y = 0; y < height; y ++) {
+ png_write_row(png_ptr,
+ const_cast<unsigned char*>(&input[y * row_byte_width]));
+ }
+ } else {
+ // Needs conversion using a separate buffer.
+ unsigned char* row = new unsigned char[width * output_color_components];
+ for (int y = 0; y < height; y ++) {
+ converter(&input[y * row_byte_width], width, row, NULL);
+ png_write_row(png_ptr, row);
+ }
+ delete[] row;
+ }
+
+ png_write_end(png_ptr, info_ptr);
+ return true;
+}
+
+} // namespace
+
+// static
+bool PNGCodec::Encode(const unsigned char* input, ColorFormat format,
+ int w, int h, int row_byte_width,
+ bool discard_transparency,
+ std::vector<unsigned char>* output) {
+ // Run to convert an input row into the output row format, NULL means no
+ // conversion is necessary.
+ FormatConverter converter = NULL;
+
+ int input_color_components, output_color_components;
+ int png_output_color_type;
+ switch (format) {
+ case FORMAT_RGB:
+ input_color_components = 3;
+ output_color_components = 3;
+ png_output_color_type = PNG_COLOR_TYPE_RGB;
+ discard_transparency = false;
+ break;
+
+ case FORMAT_RGBA:
+ input_color_components = 4;
+ if (discard_transparency) {
+ output_color_components = 3;
+ png_output_color_type = PNG_COLOR_TYPE_RGB;
+ converter = ConvertRGBAtoRGB;
+ } else {
+ output_color_components = 4;
+ png_output_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
+ converter = NULL;
+ }
+ break;
+
+ case FORMAT_BGRA:
+ input_color_components = 4;
+ if (discard_transparency) {
+ output_color_components = 3;
+ png_output_color_type = PNG_COLOR_TYPE_RGB;
+ converter = ConvertBGRAtoRGB;
+ } else {
+ output_color_components = 4;
+ png_output_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
+ converter = ConvertBetweenBGRAandRGBA;
+ }
+ break;
+
+ case FORMAT_SkBitmap:
+ input_color_components = 4;
+ if (discard_transparency) {
+ output_color_components = 3;
+ png_output_color_type = PNG_COLOR_TYPE_RGB;
+ converter = ConvertSkiatoRGB;
+ } else {
+ output_color_components = 4;
+ png_output_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
+ converter = ConvertSkiatoRGBA;
+ }
+ break;
+
+ default:
+ NOTREACHED() << "Unknown pixel format";
+ return false;
+ }
+
+ // Row stride should be at least as long as the length of the data.
+ DCHECK(input_color_components * w <= row_byte_width);
+
+ png_struct* png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
+ NULL, NULL, NULL);
+ if (!png_ptr)
+ return false;
+ png_info* info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr) {
+ png_destroy_write_struct(&png_ptr, NULL);
+ return false;
+ }
+
+ PngEncoderState state(output);
+ bool success = DoLibpngWrite(png_ptr, info_ptr, &state,
+ w, h, row_byte_width, input,
+ png_output_color_type, output_color_components,
+ converter);
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+
+ return success;
+}
+
+// static
+bool PNGCodec::EncodeBGRASkBitmap(const SkBitmap& input,
+ bool discard_transparency,
+ std::vector<unsigned char>* output) {
+ static const int bbp = 4;
+
+ SkAutoLockPixels lock_input(input);
+ DCHECK(input.empty() || input.bytesPerPixel() == bbp);
+
+ return Encode(reinterpret_cast<unsigned char*>(input.getAddr32(0, 0)),
+ FORMAT_SkBitmap, input.width(), input.height(),
+ input.width() * bbp, discard_transparency, output);
+}
+
+} // namespace gfx
diff --git a/ui/gfx/codec/png_codec.h b/ui/gfx/codec/png_codec.h
new file mode 100644
index 0000000..73453fe
--- /dev/null
+++ b/ui/gfx/codec/png_codec.h
@@ -0,0 +1,105 @@
+// 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 UI_GFX_CODEC_PNG_CODEC_H_
+#define UI_GFX_CODEC_PNG_CODEC_H_
+#pragma once
+
+#include <vector>
+
+#include "base/basictypes.h"
+
+class SkBitmap;
+
+namespace gfx {
+
+// Interface for encoding and decoding PNG data. This is a wrapper around
+// libpng, which has an inconvenient interface for callers. This is currently
+// designed for use in tests only (where we control the files), so the handling
+// isn't as robust as would be required for a browser (see Decode() for more).
+// WebKit has its own more complicated PNG decoder which handles, among other
+// things, partially downloaded data.
+class PNGCodec {
+ public:
+ enum ColorFormat {
+ // 3 bytes per pixel (packed), in RGB order regardless of endianness.
+ // This is the native JPEG format.
+ FORMAT_RGB,
+
+ // 4 bytes per pixel, in RGBA order in memory regardless of endianness.
+ FORMAT_RGBA,
+
+ // 4 bytes per pixel, in BGRA order in memory regardless of endianness.
+ // This is the default Windows DIB order.
+ FORMAT_BGRA,
+
+ // 4 bytes per pixel, in pre-multiplied kARGB_8888_Config format. For use
+ // with directly writing to a skia bitmap.
+ FORMAT_SkBitmap
+ };
+
+ // Encodes the given raw 'input' data, with each pixel being represented as
+ // given in 'format'. The encoded PNG data will be written into the supplied
+ // vector and true will be returned on success. On failure (false), the
+ // contents of the output buffer are undefined.
+ //
+ // When writing alpha values, the input colors are assumed to be post
+ // multiplied.
+ //
+ // w, h: dimensions of the image
+ // row_byte_width: the width in bytes of each row. This may be greater than
+ // w * bytes_per_pixel if there is extra padding at the end of each row
+ // (often, each row is padded to the next machine word).
+ // discard_transparency: when true, and when the input data format includes
+ // alpha values, these alpha values will be discarded and only RGB will be
+ // written to the resulting file. Otherwise, alpha values in the input
+ // will be preserved.
+ static bool Encode(const unsigned char* input, ColorFormat format,
+ int w, int h, int row_byte_width,
+ bool discard_transparency,
+ std::vector<unsigned char>* output);
+
+ // Call PNGCodec::Encode on the supplied SkBitmap |input|, which is assumed
+ // to be BGRA, 32 bits per pixel. The params |discard_transparency| and
+ // |output| are passed directly to Encode; refer to Encode for more
+ // information. During the call, an SkAutoLockPixels lock is held on |input|.
+ static bool EncodeBGRASkBitmap(const SkBitmap& input,
+ bool discard_transparency,
+ std::vector<unsigned char>* output);
+
+ // Decodes the PNG data contained in input of length input_size. The
+ // decoded data will be placed in *output with the dimensions in *w and *h
+ // on success (returns true). This data will be written in the 'format'
+ // format. On failure, the values of these output variables are undefined.
+ //
+ // This function may not support all PNG types, and it hasn't been tested
+ // with a large number of images, so assume a new format may not work. It's
+ // really designed to be able to read in something written by Encode() above.
+ static bool Decode(const unsigned char* input, size_t input_size,
+ ColorFormat format, std::vector<unsigned char>* output,
+ int* w, int* h);
+
+ // Decodes the PNG data directly into the passed in SkBitmap. This is
+ // significantly faster than the vector<unsigned char> version of Decode()
+ // above when dealing with PNG files that are >500K, which a lot of theme
+ // images are. (There are a lot of themes that have a NTP image of about ~1
+ // megabyte, and those require a 7-10 megabyte side buffer.)
+ //
+ // Returns true if data is non-null and can be decoded as a png, false
+ // otherwise.
+ static bool Decode(const unsigned char* input, size_t input_size,
+ SkBitmap* bitmap);
+
+ // Create a SkBitmap from a decoded BGRA DIB. The caller owns the returned
+ // SkBitmap.
+ static SkBitmap* CreateSkBitmapFromBGRAFormat(
+ std::vector<unsigned char>& bgra, int width, int height);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PNGCodec);
+};
+
+} // namespace gfx
+
+#endif // UI_GFX_CODEC_PNG_CODEC_H_
diff --git a/ui/gfx/codec/png_codec_unittest.cc b/ui/gfx/codec/png_codec_unittest.cc
new file mode 100644
index 0000000..19d1f19
--- /dev/null
+++ b/ui/gfx/codec/png_codec_unittest.cc
@@ -0,0 +1,297 @@
+// 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 <math.h>
+
+#include "gfx/codec/png_codec.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkUnPreMultiply.h"
+
+namespace gfx {
+
+static void MakeRGBImage(int w, int h, std::vector<unsigned char>* dat) {
+ dat->resize(w * h * 3);
+ for (int y = 0; y < h; y++) {
+ for (int x = 0; x < w; x++) {
+ unsigned char* org_px = &(*dat)[(y * w + x) * 3];
+ org_px[0] = x * 3; // r
+ org_px[1] = x * 3 + 1; // g
+ org_px[2] = x * 3 + 2; // b
+ }
+ }
+}
+
+// Set use_transparency to write data into the alpha channel, otherwise it will
+// be filled with 0xff. With the alpha channel stripped, this should yield the
+// same image as MakeRGBImage above, so the code below can make reference
+// images for conversion testing.
+static void MakeRGBAImage(int w, int h, bool use_transparency,
+ std::vector<unsigned char>* dat) {
+ dat->resize(w * h * 4);
+ for (int y = 0; y < h; y++) {
+ for (int x = 0; x < w; x++) {
+ unsigned char* org_px = &(*dat)[(y * w + x) * 4];
+ org_px[0] = x * 3; // r
+ org_px[1] = x * 3 + 1; // g
+ org_px[2] = x * 3 + 2; // b
+ if (use_transparency)
+ org_px[3] = x*3 + 3; // a
+ else
+ org_px[3] = 0xFF; // a (opaque)
+ }
+ }
+}
+
+// Returns true if each channel of the given two colors are "close." This is
+// used for comparing colors where rounding errors may cause off-by-one.
+bool ColorsClose(uint32_t a, uint32_t b) {
+ return abs(static_cast<int>(SkColorGetB(a) - SkColorGetB(b))) < 2 &&
+ abs(static_cast<int>(SkColorGetG(a) - SkColorGetG(b))) < 2 &&
+ abs(static_cast<int>(SkColorGetR(a) - SkColorGetR(b))) < 2 &&
+ abs(static_cast<int>(SkColorGetA(a) - SkColorGetA(b))) < 2;
+}
+
+// Returns true if the RGB components are "close."
+bool NonAlphaColorsClose(uint32_t a, uint32_t b) {
+ return abs(static_cast<int>(SkColorGetB(a) - SkColorGetB(b))) < 2 &&
+ abs(static_cast<int>(SkColorGetG(a) - SkColorGetG(b))) < 2 &&
+ abs(static_cast<int>(SkColorGetR(a) - SkColorGetR(b))) < 2;
+}
+
+void MakeTestSkBitmap(int w, int h, SkBitmap* bmp) {
+ bmp->setConfig(SkBitmap::kARGB_8888_Config, w, h);
+ bmp->allocPixels();
+
+ uint32_t* src_data = bmp->getAddr32(0, 0);
+ for (int i = 0; i < w * h; i++) {
+ src_data[i] = SkPreMultiplyARGB(i % 255, i % 250, i % 245, i % 240);
+ }
+}
+
+TEST(PNGCodec, EncodeDecodeRGB) {
+ const int w = 20, h = 20;
+
+ // create an image with known values
+ std::vector<unsigned char> original;
+ MakeRGBImage(w, h, &original);
+
+ // encode
+ std::vector<unsigned char> encoded;
+ EXPECT_TRUE(PNGCodec::Encode(&original[0], PNGCodec::FORMAT_RGB, w, h,
+ w * 3, false, &encoded));
+
+ // decode, it should have the same size as the original
+ std::vector<unsigned char> decoded;
+ int outw, outh;
+ EXPECT_TRUE(PNGCodec::Decode(&encoded[0], encoded.size(),
+ PNGCodec::FORMAT_RGB, &decoded,
+ &outw, &outh));
+ ASSERT_EQ(w, outw);
+ ASSERT_EQ(h, outh);
+ ASSERT_EQ(original.size(), decoded.size());
+
+ // Images must be equal
+ ASSERT_TRUE(original == decoded);
+}
+
+TEST(PNGCodec, EncodeDecodeRGBA) {
+ const int w = 20, h = 20;
+
+ // create an image with known values, a must be opaque because it will be
+ // lost during encoding
+ std::vector<unsigned char> original;
+ MakeRGBAImage(w, h, true, &original);
+
+ // encode
+ std::vector<unsigned char> encoded;
+ EXPECT_TRUE(PNGCodec::Encode(&original[0], PNGCodec::FORMAT_RGBA, w, h,
+ w * 4, false, &encoded));
+
+ // decode, it should have the same size as the original
+ std::vector<unsigned char> decoded;
+ int outw, outh;
+ EXPECT_TRUE(PNGCodec::Decode(&encoded[0], encoded.size(),
+ PNGCodec::FORMAT_RGBA, &decoded,
+ &outw, &outh));
+ ASSERT_EQ(w, outw);
+ ASSERT_EQ(h, outh);
+ ASSERT_EQ(original.size(), decoded.size());
+
+ // Images must be exactly equal
+ ASSERT_TRUE(original == decoded);
+}
+
+// Test that corrupted data decompression causes failures.
+TEST(PNGCodec, DecodeCorrupted) {
+ int w = 20, h = 20;
+
+ // Make some random data (an uncompressed image).
+ std::vector<unsigned char> original;
+ MakeRGBImage(w, h, &original);
+
+ // It should fail when given non-JPEG compressed data.
+ std::vector<unsigned char> output;
+ int outw, outh;
+ EXPECT_FALSE(PNGCodec::Decode(&original[0], original.size(),
+ PNGCodec::FORMAT_RGB, &output,
+ &outw, &outh));
+
+ // Make some compressed data.
+ std::vector<unsigned char> compressed;
+ EXPECT_TRUE(PNGCodec::Encode(&original[0], PNGCodec::FORMAT_RGB, w, h,
+ w * 3, false, &compressed));
+
+ // Try decompressing a truncated version.
+ EXPECT_FALSE(PNGCodec::Decode(&compressed[0], compressed.size() / 2,
+ PNGCodec::FORMAT_RGB, &output,
+ &outw, &outh));
+
+ // Corrupt it and try decompressing that.
+ for (int i = 10; i < 30; i++)
+ compressed[i] = i;
+ EXPECT_FALSE(PNGCodec::Decode(&compressed[0], compressed.size(),
+ PNGCodec::FORMAT_RGB, &output,
+ &outw, &outh));
+}
+
+TEST(PNGCodec, EncodeDecodeBGRA) {
+ const int w = 20, h = 20;
+
+ // Create an image with known values, alpha must be opaque because it will be
+ // lost during encoding.
+ std::vector<unsigned char> original;
+ MakeRGBAImage(w, h, true, &original);
+
+ // Encode.
+ std::vector<unsigned char> encoded;
+ EXPECT_TRUE(PNGCodec::Encode(&original[0], PNGCodec::FORMAT_BGRA, w, h,
+ w * 4, false, &encoded));
+
+ // Decode, it should have the same size as the original.
+ std::vector<unsigned char> decoded;
+ int outw, outh;
+ EXPECT_TRUE(PNGCodec::Decode(&encoded[0], encoded.size(),
+ PNGCodec::FORMAT_BGRA, &decoded,
+ &outw, &outh));
+ ASSERT_EQ(w, outw);
+ ASSERT_EQ(h, outh);
+ ASSERT_EQ(original.size(), decoded.size());
+
+ // Images must be exactly equal.
+ ASSERT_TRUE(original == decoded);
+}
+
+TEST(PNGCodec, StripAddAlpha) {
+ const int w = 20, h = 20;
+
+ // These should be the same except one has a 0xff alpha channel.
+ std::vector<unsigned char> original_rgb;
+ MakeRGBImage(w, h, &original_rgb);
+ std::vector<unsigned char> original_rgba;
+ MakeRGBAImage(w, h, false, &original_rgba);
+
+ // Encode RGBA data as RGB.
+ std::vector<unsigned char> encoded;
+ EXPECT_TRUE(PNGCodec::Encode(&original_rgba[0],
+ PNGCodec::FORMAT_RGBA,
+ w, h,
+ w * 4, true, &encoded));
+
+ // Decode the RGB to RGBA.
+ std::vector<unsigned char> decoded;
+ int outw, outh;
+ EXPECT_TRUE(PNGCodec::Decode(&encoded[0], encoded.size(),
+ PNGCodec::FORMAT_RGBA, &decoded,
+ &outw, &outh));
+
+ // Decoded and reference should be the same (opaque alpha).
+ ASSERT_EQ(w, outw);
+ ASSERT_EQ(h, outh);
+ ASSERT_EQ(original_rgba.size(), decoded.size());
+ ASSERT_TRUE(original_rgba == decoded);
+
+ // Encode RGBA to RGBA.
+ EXPECT_TRUE(PNGCodec::Encode(&original_rgba[0],
+ PNGCodec::FORMAT_RGBA,
+ w, h,
+ w * 4, false, &encoded));
+
+ // Decode the RGBA to RGB.
+ EXPECT_TRUE(PNGCodec::Decode(&encoded[0], encoded.size(),
+ PNGCodec::FORMAT_RGB, &decoded,
+ &outw, &outh));
+
+ // It should be the same as our non-alpha-channel reference.
+ ASSERT_EQ(w, outw);
+ ASSERT_EQ(h, outh);
+ ASSERT_EQ(original_rgb.size(), decoded.size());
+ ASSERT_TRUE(original_rgb == decoded);
+}
+
+TEST(PNGCodec, EncodeBGRASkBitmap) {
+ const int w = 20, h = 20;
+
+ SkBitmap original_bitmap;
+ MakeTestSkBitmap(w, h, &original_bitmap);
+
+ // Encode the bitmap.
+ std::vector<unsigned char> encoded;
+ PNGCodec::EncodeBGRASkBitmap(original_bitmap, false, &encoded);
+
+ // Decode the encoded string.
+ SkBitmap decoded_bitmap;
+ EXPECT_TRUE(PNGCodec::Decode(&encoded.front(), encoded.size(),
+ &decoded_bitmap));
+
+ // Compare the original bitmap and the output bitmap. We use ColorsClose
+ // as SkBitmaps are considered to be pre-multiplied, the unpremultiplication
+ // (in Encode) and repremultiplication (in Decode) can be lossy.
+ for (int x = 0; x < w; x++) {
+ for (int y = 0; y < h; y++) {
+ uint32_t original_pixel = original_bitmap.getAddr32(0, y)[x];
+ uint32_t decoded_pixel = decoded_bitmap.getAddr32(0, y)[x];
+ EXPECT_TRUE(ColorsClose(original_pixel, decoded_pixel));
+ }
+ }
+}
+
+TEST(PNGCodec, EncodeBGRASkBitmapDiscardTransparency) {
+ const int w = 20, h = 20;
+
+ SkBitmap original_bitmap;
+ MakeTestSkBitmap(w, h, &original_bitmap);
+
+ // Encode the bitmap.
+ std::vector<unsigned char> encoded;
+ PNGCodec::EncodeBGRASkBitmap(original_bitmap, true, &encoded);
+
+ // Decode the encoded string.
+ SkBitmap decoded_bitmap;
+ EXPECT_TRUE(PNGCodec::Decode(&encoded.front(), encoded.size(),
+ &decoded_bitmap));
+
+ // Compare the original bitmap and the output bitmap. We need to
+ // unpremultiply original_pixel, as the decoded bitmap doesn't have an alpha
+ // channel.
+ for (int x = 0; x < w; x++) {
+ for (int y = 0; y < h; y++) {
+ uint32_t original_pixel = original_bitmap.getAddr32(0, y)[x];
+ uint32_t unpremultiplied =
+ SkUnPreMultiply::PMColorToColor(original_pixel);
+ uint32_t decoded_pixel = decoded_bitmap.getAddr32(0, y)[x];
+ EXPECT_TRUE(NonAlphaColorsClose(unpremultiplied, decoded_pixel))
+ << "Original_pixel: ("
+ << SkColorGetR(unpremultiplied) << ", "
+ << SkColorGetG(unpremultiplied) << ", "
+ << SkColorGetB(unpremultiplied) << "), "
+ << "Decoded pixel: ("
+ << SkColorGetR(decoded_pixel) << ", "
+ << SkColorGetG(decoded_pixel) << ", "
+ << SkColorGetB(decoded_pixel) << ")";
+ }
+ }
+}
+
+} // namespace gfx
diff --git a/ui/gfx/color_utils.cc b/ui/gfx/color_utils.cc
new file mode 100644
index 0000000..73c585b
--- /dev/null
+++ b/ui/gfx/color_utils.cc
@@ -0,0 +1,300 @@
+// 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/color_utils.h"
+
+#include <math.h>
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
+#include <algorithm>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "build/build_config.h"
+#if defined(OS_WIN)
+#include "skia/ext/skia_utils_win.h"
+#endif
+#include "third_party/skia/include/core/SkBitmap.h"
+
+namespace color_utils {
+
+// Helper functions -----------------------------------------------------------
+
+namespace {
+
+double calcHue(double temp1, double temp2, double hue) {
+ if (hue < 0.0)
+ ++hue;
+ else if (hue > 1.0)
+ --hue;
+
+ if (hue * 6.0 < 1.0)
+ return temp1 + (temp2 - temp1) * hue * 6.0;
+ if (hue * 2.0 < 1.0)
+ return temp2;
+ if (hue * 3.0 < 2.0)
+ return temp1 + (temp2 - temp1) * (2.0 / 3.0 - hue) * 6.0;
+
+ return temp1;
+}
+
+int GetLumaForColor(SkColor* color) {
+ int luma = static_cast<int>((0.3 * SkColorGetR(*color)) +
+ (0.59 * SkColorGetG(*color)) +
+ (0.11 * SkColorGetB(*color)));
+ return std::max(std::min(luma, 255), 0);
+}
+
+// Next two functions' formulas from:
+// http://www.w3.org/TR/WCAG20/#relativeluminancedef
+// http://www.w3.org/TR/WCAG20/#contrast-ratiodef
+
+double ConvertSRGB(double eight_bit_component) {
+ const double component = eight_bit_component / 255.0;
+ return (component <= 0.03928) ?
+ (component / 12.92) : pow((component + 0.055) / 1.055, 2.4);
+}
+
+SkColor LumaInvertColor(const SkColor& color) {
+ HSL hsl;
+ SkColorToHSL(color, &hsl);
+ hsl.l = 1.0 - hsl.l;
+ return HSLToSkColor(hsl, 255);
+}
+
+double ContrastRatio(double foreground_luminance, double background_luminance) {
+ // NOTE: Only pass in numbers obtained from RelativeLuminance(), since those
+ // are guaranteed to be > 0 and thus not cause a divide-by-zero error here.
+ return (foreground_luminance > background_luminance) ?
+ (foreground_luminance / background_luminance) :
+ (background_luminance / foreground_luminance);
+}
+
+} // namespace
+
+// ----------------------------------------------------------------------------
+
+double RelativeLuminance(SkColor color) {
+ return (0.2126 * ConvertSRGB(SkColorGetR(color))) +
+ (0.7152 * ConvertSRGB(SkColorGetG(color))) +
+ (0.0722 * ConvertSRGB(SkColorGetB(color))) + 0.05;
+}
+
+void SkColorToHSL(SkColor c, HSL* hsl) {
+ double r = static_cast<double>(SkColorGetR(c)) / 255.0;
+ double g = static_cast<double>(SkColorGetG(c)) / 255.0;
+ double b = static_cast<double>(SkColorGetB(c)) / 255.0;
+ double vmax = std::max(std::max(r, g), b);
+ double vmin = std::min(std::min(r, g), b);
+ double delta = vmax - vmin;
+ hsl->l = (vmax + vmin) / 2;
+ if (delta) {
+ double dr = (((vmax - r) / 6.0) + (delta / 2.0)) / delta;
+ double dg = (((vmax - g) / 6.0) + (delta / 2.0)) / delta;
+ double db = (((vmax - b) / 6.0) + (delta / 2.0)) / delta;
+ // We need to compare for the max value because comparing vmax to r,
+ // g or b can sometimes result in values overflowing registers.
+ if (r >= g && r >= b)
+ hsl->h = db - dg;
+ else if (g >= r && g >= b)
+ hsl->h = (1.0 / 3.0) + dr - db;
+ else // (b >= r && b >= g)
+ hsl->h = (2.0 / 3.0) + dg - dr;
+
+ if (hsl->h < 0.0)
+ ++hsl->h;
+ else if (hsl->h > 1.0)
+ --hsl->h;
+
+ hsl->s = delta / ((hsl->l < 0.5) ? (vmax + vmin) : (2 - vmax - vmin));
+ } else {
+ hsl->h = hsl->s = 0;
+ }
+}
+
+SkColor HSLToSkColor(const HSL& hsl, SkAlpha alpha) {
+ double hue = hsl.h;
+ double saturation = hsl.s;
+ double lightness = hsl.l;
+
+ // If there's no color, we don't care about hue and can do everything based
+ // on brightness.
+ if (!saturation) {
+ uint8 light;
+
+ if (lightness < 0)
+ light = 0;
+ else if (lightness >= 1.0)
+ light = 255;
+ else
+ light = SkDoubleToFixed(lightness) >> 8;
+
+ return SkColorSetARGB(alpha, light, light, light);
+ }
+
+ double temp2 = (lightness < 0.5) ?
+ (lightness * (1.0 + saturation)) :
+ (lightness + saturation - (lightness * saturation));
+ double temp1 = 2.0 * lightness - temp2;
+ return SkColorSetARGB(alpha,
+ static_cast<int>(calcHue(temp1, temp2, hue + 1.0 / 3.0) * 255),
+ static_cast<int>(calcHue(temp1, temp2, hue) * 255),
+ static_cast<int>(calcHue(temp1, temp2, hue - 1.0 / 3.0) * 255));
+}
+
+SkColor HSLShift(SkColor color, const HSL& shift) {
+ HSL hsl;
+ int alpha = SkColorGetA(color);
+ SkColorToHSL(color, &hsl);
+
+ // Replace the hue with the tint's hue.
+ if (shift.h >= 0)
+ hsl.h = shift.h;
+
+ // Change the saturation.
+ if (shift.s >= 0) {
+ if (shift.s <= 0.5)
+ hsl.s *= shift.s * 2.0;
+ else
+ hsl.s += (1.0 - hsl.s) * ((shift.s - 0.5) * 2.0);
+ }
+
+ SkColor result = HSLToSkColor(hsl, alpha);
+
+ if (shift.l < 0)
+ return result;
+
+ // Lightness shifts in the style of popular image editors aren't
+ // actually represented in HSL - the L value does have some effect
+ // on saturation.
+ double r = static_cast<double>(SkColorGetR(result));
+ double g = static_cast<double>(SkColorGetG(result));
+ double b = static_cast<double>(SkColorGetB(result));
+ if (shift.l <= 0.5) {
+ r *= (shift.l * 2.0);
+ g *= (shift.l * 2.0);
+ b *= (shift.l * 2.0);
+ } else {
+ r += (255.0 - r) * ((shift.l - 0.5) * 2.0);
+ g += (255.0 - g) * ((shift.l - 0.5) * 2.0);
+ b += (255.0 - b) * ((shift.l - 0.5) * 2.0);
+ }
+ return SkColorSetARGB(alpha,
+ static_cast<int>(r),
+ static_cast<int>(g),
+ static_cast<int>(b));
+}
+
+bool IsColorCloseToTransparent(SkAlpha alpha) {
+ const int kCloseToBoundary = 64;
+ return alpha < kCloseToBoundary;
+}
+
+bool IsColorCloseToGrey(int r, int g, int b) {
+ const int kAverageBoundary = 15;
+ int average = (r + g + b) / 3;
+ return (abs(r - average) < kAverageBoundary) &&
+ (abs(g - average) < kAverageBoundary) &&
+ (abs(b - average) < kAverageBoundary);
+}
+
+SkColor GetAverageColorOfFavicon(SkBitmap* favicon, SkAlpha alpha) {
+ int r = 0, g = 0, b = 0;
+
+ SkAutoLockPixels favicon_lock(*favicon);
+ SkColor* pixels = static_cast<SkColor*>(favicon->getPixels());
+ // Assume ARGB_8888 format.
+ DCHECK(favicon->getConfig() == SkBitmap::kARGB_8888_Config);
+ SkColor* current_color = pixels;
+
+ DCHECK(favicon->width() <= 16 && favicon->height() <= 16);
+
+ int pixel_count = favicon->width() * favicon->height();
+ int color_count = 0;
+ for (int i = 0; i < pixel_count; ++i, ++current_color) {
+ // Disregard this color if it is close to black, close to white, or close
+ // to transparent since any of those pixels do not contribute much to the
+ // color makeup of this icon.
+ int cr = SkColorGetR(*current_color);
+ int cg = SkColorGetG(*current_color);
+ int cb = SkColorGetB(*current_color);
+
+ if (IsColorCloseToTransparent(SkColorGetA(*current_color)) ||
+ IsColorCloseToGrey(cr, cg, cb))
+ continue;
+
+ r += cr;
+ g += cg;
+ b += cb;
+ ++color_count;
+ }
+
+ return color_count ?
+ SkColorSetARGB(alpha, r / color_count, g / color_count, b / color_count) :
+ SkColorSetARGB(alpha, 0, 0, 0);
+}
+
+void BuildLumaHistogram(SkBitmap* bitmap, int histogram[256]) {
+ SkAutoLockPixels bitmap_lock(*bitmap);
+ // Assume ARGB_8888 format.
+ DCHECK(bitmap->getConfig() == SkBitmap::kARGB_8888_Config);
+
+ int pixel_width = bitmap->width();
+ int pixel_height = bitmap->height();
+ for (int y = 0; y < pixel_height; ++y) {
+ SkColor* current_color = static_cast<uint32_t*>(bitmap->getAddr32(0, y));
+ for (int x = 0; x < pixel_width; ++x, ++current_color)
+ histogram[GetLumaForColor(current_color)]++;
+ }
+}
+
+SkColor AlphaBlend(SkColor foreground, SkColor background, SkAlpha alpha) {
+ if (alpha == 0)
+ return background;
+ if (alpha == 255)
+ return foreground;
+
+ int f_alpha = SkColorGetA(foreground);
+ int b_alpha = SkColorGetA(background);
+
+ double normalizer = (f_alpha * alpha + b_alpha * (255 - alpha)) / 255.0;
+ if (normalizer == 0.0)
+ return SkColorSetARGB(0, 0, 0, 0);
+
+ double f_weight = f_alpha * alpha / normalizer;
+ double b_weight = b_alpha * (255 - alpha) / normalizer;
+
+ double r = (SkColorGetR(foreground) * f_weight +
+ SkColorGetR(background) * b_weight) / 255.0;
+ double g = (SkColorGetG(foreground) * f_weight +
+ SkColorGetG(background) * b_weight) / 255.0;
+ double b = (SkColorGetB(foreground) * f_weight +
+ SkColorGetB(background) * b_weight) / 255.0;
+
+ return SkColorSetARGB(static_cast<int>(normalizer),
+ static_cast<int>(r),
+ static_cast<int>(g),
+ static_cast<int>(b));
+}
+
+SkColor GetReadableColor(SkColor foreground, SkColor background) {
+ const SkColor foreground2 = LumaInvertColor(foreground);
+ const double background_luminance = RelativeLuminance(background);
+ return (ContrastRatio(RelativeLuminance(foreground), background_luminance) >=
+ ContrastRatio(RelativeLuminance(foreground2), background_luminance)) ?
+ foreground : foreground2;
+}
+
+SkColor GetSysSkColor(int which) {
+#if defined(OS_WIN)
+ return skia::COLORREFToSkColor(GetSysColor(which));
+#else
+ NOTIMPLEMENTED();
+ return SK_ColorLTGRAY;
+#endif
+}
+
+} // namespace color_utils
diff --git a/ui/gfx/color_utils.h b/ui/gfx/color_utils.h
new file mode 100644
index 0000000..a043b82
--- /dev/null
+++ b/ui/gfx/color_utils.h
@@ -0,0 +1,80 @@
+// 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.
+
+#ifndef UI_GFX_COLOR_UTILS_H_
+#define UI_GFX_COLOR_UTILS_H_
+#pragma once
+
+#include "third_party/skia/include/core/SkColor.h"
+
+class SkBitmap;
+
+namespace color_utils {
+
+// Represents an HSL color.
+struct HSL {
+ double h;
+ double s;
+ double l;
+};
+
+// Calculated according to http://www.w3.org/TR/WCAG20/#relativeluminancedef
+double RelativeLuminance(SkColor color);
+
+// Note: these transformations assume sRGB as the source color space
+void SkColorToHSL(SkColor c, HSL* hsl);
+SkColor HSLToSkColor(const HSL& hsl, SkAlpha alpha);
+
+// HSL-Shift an SkColor. The shift values are in the range of 0-1, with the
+// option to specify -1 for 'no change'. The shift values are defined as:
+// hsl_shift[0] (hue): The absolute hue value - 0 and 1 map
+// to 0 and 360 on the hue color wheel (red).
+// hsl_shift[1] (saturation): A saturation shift, with the
+// following key values:
+// 0 = remove all color.
+// 0.5 = leave unchanged.
+// 1 = fully saturate the image.
+// hsl_shift[2] (lightness): A lightness shift, with the
+// following key values:
+// 0 = remove all lightness (make all pixels black).
+// 0.5 = leave unchanged.
+// 1 = full lightness (make all pixels white).
+SkColor HSLShift(SkColor color, const HSL& shift);
+
+// Determine if a given alpha value is nearly completely transparent.
+bool IsColorCloseToTransparent(SkAlpha alpha);
+
+// Determine if a color is near grey.
+bool IsColorCloseToGrey(int r, int g, int b);
+
+// Gets a color representing a bitmap. The definition of "representing" is the
+// average color in the bitmap. The color returned is modified to have the
+// specified alpha.
+SkColor GetAverageColorOfFavicon(SkBitmap* bitmap, SkAlpha alpha);
+
+// Builds a histogram based on the Y' of the Y'UV representation of
+// this image.
+void BuildLumaHistogram(SkBitmap* bitmap, int histogram[256]);
+
+// Returns a blend of the supplied colors, ranging from |background| (for
+// |alpha| == 0) to |foreground| (for |alpha| == 255). The alpha channels of
+// the supplied colors are also taken into account, so the returned color may
+// be partially transparent.
+SkColor AlphaBlend(SkColor foreground, SkColor background, SkAlpha alpha);
+
+// Given a foreground and background color, try to return a foreground color
+// that is "readable" over the background color by luma-inverting the foreground
+// color and then picking whichever foreground color has higher contrast against
+// the background color.
+//
+// NOTE: This won't do anything but waste time if the supplied foreground color
+// has a luma value close to the midpoint (0.5 in the HSL representation).
+SkColor GetReadableColor(SkColor foreground, SkColor background);
+
+// Gets a Windows system color as a SkColor
+SkColor GetSysSkColor(int which);
+
+} // namespace color_utils
+
+#endif // UI_GFX_COLOR_UTILS_H_
diff --git a/ui/gfx/color_utils_unittest.cc b/ui/gfx/color_utils_unittest.cc
new file mode 100644
index 0000000..30cf514
--- /dev/null
+++ b/ui/gfx/color_utils_unittest.cc
@@ -0,0 +1,67 @@
+// 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 <stdlib.h>
+
+#include "gfx/color_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkColorPriv.h"
+
+TEST(ColorUtils, SkColorToHSLRed) {
+ color_utils::HSL hsl = { 0, 0, 0 };
+ color_utils::SkColorToHSL(SK_ColorRED, &hsl);
+ EXPECT_EQ(hsl.h, 0);
+ EXPECT_EQ(hsl.s, 1);
+ EXPECT_EQ(hsl.l, 0.5);
+}
+
+TEST(ColorUtils, SkColorToHSLGrey) {
+ color_utils::HSL hsl = { 0, 0, 0 };
+ color_utils::SkColorToHSL(SkColorSetARGB(255, 128, 128, 128), &hsl);
+ EXPECT_EQ(hsl.h, 0);
+ EXPECT_EQ(hsl.s, 0);
+ EXPECT_EQ(static_cast<int>(hsl.l * 100),
+ static_cast<int>(0.5 * 100)); // Accurate to two decimal places.
+}
+
+TEST(ColorUtils, HSLToSkColorWithAlpha) {
+ SkColor red = SkColorSetARGB(128, 255, 0, 0);
+ color_utils::HSL hsl = { 0, 1, 0.5 };
+ SkColor result = color_utils::HSLToSkColor(hsl, 128);
+ EXPECT_EQ(SkColorGetA(red), SkColorGetA(result));
+ EXPECT_EQ(SkColorGetR(red), SkColorGetR(result));
+ EXPECT_EQ(SkColorGetG(red), SkColorGetG(result));
+ EXPECT_EQ(SkColorGetB(red), SkColorGetB(result));
+}
+
+TEST(ColorUtils, ColorToHSLRegisterSpill) {
+ // In a opt build on Linux, this was causing a register spill on my laptop
+ // (Pentium M) when converting from SkColor to HSL.
+ SkColor input = SkColorSetARGB(255, 206, 154, 89);
+ color_utils::HSL hsl = { -1, -1, -1 };
+ SkColor result = color_utils::HSLShift(input, hsl);
+ EXPECT_EQ(255U, SkColorGetA(result));
+ EXPECT_EQ(206U, SkColorGetR(result));
+ EXPECT_EQ(153U, SkColorGetG(result));
+ EXPECT_EQ(88U, SkColorGetB(result));
+}
+
+TEST(ColorUtils, AlphaBlend) {
+ SkColor fore = SkColorSetARGB(255, 200, 200, 200);
+ SkColor back = SkColorSetARGB(255, 100, 100, 100);
+
+ EXPECT_TRUE(color_utils::AlphaBlend(fore, back, 255) ==
+ fore);
+ EXPECT_TRUE(color_utils::AlphaBlend(fore, back, 0) ==
+ back);
+
+ // One is fully transparent, result is partially transparent.
+ back = SkColorSetA(back, 0);
+ EXPECT_EQ(136U, SkColorGetA(color_utils::AlphaBlend(fore, back, 136)));
+
+ // Both are fully transparent, result is fully transparent.
+ fore = SkColorSetA(fore, 0);
+ EXPECT_EQ(0U, SkColorGetA(color_utils::AlphaBlend(fore, back, 255)));
+}
diff --git a/ui/gfx/empty.cc b/ui/gfx/empty.cc
new file mode 100644
index 0000000..63672f6
--- /dev/null
+++ b/ui/gfx/empty.cc
@@ -0,0 +1,6 @@
+// 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.
+
+void MacSux() {
+}
diff --git a/ui/gfx/favicon_size.h b/ui/gfx/favicon_size.h
new file mode 100644
index 0000000..5e9cd55
--- /dev/null
+++ b/ui/gfx/favicon_size.h
@@ -0,0 +1,34 @@
+// 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 UI_GFX_FAVICON_SIZE_H_
+#define UI_GFX_FAVICON_SIZE_H_
+#pragma once
+
+#include "base/compiler_specific.h"
+
+// Size (along each axis) of the favicon.
+const int kFavIconSize = 16;
+
+// If the width or height is bigger than the favicon size, a new width/height
+// is calculated and returned in width/height that maintains the aspect
+// ratio of the supplied values.
+static void calc_favicon_target_size(int* width, int* height) ALLOW_UNUSED;
+
+// static
+void calc_favicon_target_size(int* width, int* height) {
+ if (*width > kFavIconSize || *height > kFavIconSize) {
+ // Too big, resize it maintaining the aspect ratio.
+ float aspect_ratio = static_cast<float>(*width) /
+ static_cast<float>(*height);
+ *height = kFavIconSize;
+ *width = static_cast<int>(aspect_ratio * *height);
+ if (*width > kFavIconSize) {
+ *width = kFavIconSize;
+ *height = static_cast<int>(*width / aspect_ratio);
+ }
+ }
+}
+
+#endif // UI_GFX_FAVICON_SIZE_H_
diff --git a/ui/gfx/font.cc b/ui/gfx/font.cc
new file mode 100644
index 0000000..78b1a34
--- /dev/null
+++ b/ui/gfx/font.cc
@@ -0,0 +1,85 @@
+// 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 "base/utf_string_conversions.h"
+#include "gfx/platform_font.h"
+
+namespace gfx {
+
+////////////////////////////////////////////////////////////////////////////////
+// Font, public:
+
+Font::Font() : platform_font_(PlatformFont::CreateDefault()) {
+}
+
+Font::Font(const Font& other) : platform_font_(other.platform_font_) {
+}
+
+gfx::Font& Font::operator=(const Font& other) {
+ platform_font_ = other.platform_font_;
+ return *this;
+}
+
+Font::Font(NativeFont native_font)
+ : platform_font_(PlatformFont::CreateFromNativeFont(native_font)) {
+}
+
+Font::Font(PlatformFont* platform_font) : platform_font_(platform_font) {
+}
+
+Font::Font(const string16& font_name, int font_size)
+ : platform_font_(PlatformFont::CreateFromNameAndSize(font_name,
+ font_size)) {
+}
+
+Font::~Font() {
+}
+
+Font Font::DeriveFont(int size_delta) const {
+ return DeriveFont(size_delta, GetStyle());
+}
+
+Font Font::DeriveFont(int size_delta, int style) const {
+ return platform_font_->DeriveFont(size_delta, style);
+}
+
+int Font::GetHeight() const {
+ return platform_font_->GetHeight();
+}
+
+int Font::GetBaseline() const {
+ return platform_font_->GetBaseline();
+}
+
+int Font::GetAverageCharacterWidth() const {
+ return platform_font_->GetAverageCharacterWidth();
+}
+
+int Font::GetStringWidth(const string16& text) const {
+ return platform_font_->GetStringWidth(text);
+}
+
+int Font::GetExpectedTextWidth(int length) const {
+ return platform_font_->GetExpectedTextWidth(length);
+}
+
+int Font::GetStyle() const {
+ return platform_font_->GetStyle();
+}
+
+string16 Font::GetFontName() const {
+ return platform_font_->GetFontName();
+}
+
+int Font::GetFontSize() const {
+ return platform_font_->GetFontSize();
+}
+
+NativeFont Font::GetNativeFont() const {
+ return platform_font_->GetNativeFont();
+}
+
+} // namespace gfx
diff --git a/ui/gfx/font.h b/ui/gfx/font.h
new file mode 100644
index 0000000..2091ae3
--- /dev/null
+++ b/ui/gfx/font.h
@@ -0,0 +1,113 @@
+// 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 UI_GFX_FONT_H_
+#define UI_GFX_FONT_H_
+#pragma once
+
+#include <string>
+
+#include "base/ref_counted.h"
+#include "base/string16.h"
+#include "gfx/native_widget_types.h"
+
+namespace gfx {
+
+class PlatformFont;
+
+// 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 FontStyle {
+ NORMAL = 0,
+ BOLD = 1,
+ ITALIC = 2,
+ UNDERLINED = 4,
+ };
+
+ // Creates a font with the default name and style.
+ Font();
+
+ // Creates a font that is a clone of another font object.
+ Font(const Font& other);
+ gfx::Font& operator=(const Font& other);
+
+ // Creates a font from the specified native font.
+ explicit Font(NativeFont native_font);
+
+ // Construct a Font object with the specified PlatformFont object. The Font
+ // object takes ownership of the PlatformFont object.
+ explicit Font(PlatformFont* platform_font);
+
+ // Creates a font with the specified name and size.
+ Font(const string16& 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;
+
+ // 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 GetHeight() const;
+
+ // Returns the baseline, or ascent, of the font.
+ int GetBaseline() const;
+
+ // Returns the average character width for the font.
+ int GetAverageCharacterWidth() const;
+
+ // Returns the number of horizontal pixels needed to display the specified
+ // string.
+ int GetStringWidth(const string16& text) const;
+
+ // Returns the expected number of horizontal pixels needed to display the
+ // specified length of characters. Call GetStringWidth() to retrieve the
+ // actual number.
+ int GetExpectedTextWidth(int length) const;
+
+ // Returns the style of the font.
+ int GetStyle() const;
+
+ // Returns the font name.
+ string16 GetFontName() const;
+
+ // Returns the font size in pixels.
+ int GetFontSize() const;
+
+ // Returns the native font handle.
+ // Lifetime lore:
+ // Windows: This handle is owned by the Font object, and should not be
+ // destroyed by the caller.
+ // Mac: Caller must release this object.
+ // Gtk: This handle is created on demand, and must be freed by calling
+ // pango_font_description_free() when the caller is done using it.
+ NativeFont GetNativeFont() const;
+
+ // Raw access to the underlying platform font implementation. Can be
+ // static_cast to a known implementation type if needed.
+ PlatformFont* platform_font() const { return platform_font_.get(); }
+
+ private:
+ // Wrapped platform font implementation.
+ scoped_refptr<PlatformFont> platform_font_;
+};
+
+} // namespace gfx
+
+#endif // UI_GFX_FONT_H_
diff --git a/ui/gfx/font_unittest.cc b/ui/gfx/font_unittest.cc
new file mode 100644
index 0000000..43eaa7c
--- /dev/null
+++ b/ui/gfx/font_unittest.cc
@@ -0,0 +1,120 @@
+// 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/font.h"
+
+#include "base/utf_string_conversions.h"
+#if defined(OS_WIN)
+#include "gfx/platform_font_win.h"
+#endif // defined(OS_WIN)
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+using gfx::Font;
+
+class FontTest : public testing::Test {
+};
+
+#if defined(OS_WIN)
+class ScopedMinimumFontSizeCallback {
+ public:
+ explicit ScopedMinimumFontSizeCallback(int minimum_size) {
+ minimum_size_ = minimum_size;
+ old_callback_ = gfx::PlatformFontWin::get_minimum_font_size_callback;
+ gfx::PlatformFontWin::get_minimum_font_size_callback = &GetMinimumFontSize;
+ }
+
+ ~ScopedMinimumFontSizeCallback() {
+ gfx::PlatformFontWin::get_minimum_font_size_callback = old_callback_;
+ }
+
+ private:
+ static int GetMinimumFontSize() {
+ return minimum_size_;
+ }
+
+ gfx::PlatformFontWin::GetMinimumFontSizeCallback old_callback_;
+ static int minimum_size_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedMinimumFontSizeCallback);
+};
+
+int ScopedMinimumFontSizeCallback::minimum_size_ = 0;
+#endif // defined(OS_WIN)
+
+
+TEST_F(FontTest, LoadArial) {
+ Font cf(ASCIIToUTF16("Arial"), 16);
+ ASSERT_TRUE(cf.GetNativeFont());
+ ASSERT_EQ(cf.GetStyle(), Font::NORMAL);
+ ASSERT_EQ(cf.GetFontSize(), 16);
+ ASSERT_EQ(cf.GetFontName(), ASCIIToUTF16("Arial"));
+}
+
+TEST_F(FontTest, LoadArialBold) {
+ Font cf(ASCIIToUTF16("Arial"), 16);
+ Font bold(cf.DeriveFont(0, Font::BOLD));
+ ASSERT_TRUE(bold.GetNativeFont());
+ ASSERT_EQ(bold.GetStyle(), Font::BOLD);
+}
+
+TEST_F(FontTest, Ascent) {
+ Font cf(ASCIIToUTF16("Arial"), 16);
+ ASSERT_GT(cf.GetBaseline(), 2);
+ ASSERT_LE(cf.GetBaseline(), 22);
+}
+
+TEST_F(FontTest, Height) {
+ Font cf(ASCIIToUTF16("Arial"), 16);
+ ASSERT_GE(cf.GetHeight(), 16);
+ // TODO(akalin): Figure out why height is so large on Linux.
+ ASSERT_LE(cf.GetHeight(), 26);
+}
+
+TEST_F(FontTest, AvgWidths) {
+ Font cf(ASCIIToUTF16("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(ASCIIToUTF16("Arial"), 16);
+ ASSERT_EQ(cf.GetStringWidth(ASCIIToUTF16("")), 0);
+ ASSERT_GT(cf.GetStringWidth(ASCIIToUTF16("a")),
+ cf.GetStringWidth(ASCIIToUTF16("")));
+ ASSERT_GT(cf.GetStringWidth(ASCIIToUTF16("ab")),
+ cf.GetStringWidth(ASCIIToUTF16("a")));
+ ASSERT_GT(cf.GetStringWidth(ASCIIToUTF16("abc")),
+ cf.GetStringWidth(ASCIIToUTF16("ab")));
+}
+
+#if defined(OS_WIN)
+TEST_F(FontTest, DeriveFontResizesIfSizeTooSmall) {
+ // This creates font of height -8.
+ Font cf(L"Arial", 6);
+ // The minimum font size is set to 5 in browser_main.cc.
+ ScopedMinimumFontSizeCallback minimum_size(5);
+
+ Font derived_font = cf.DeriveFont(-4);
+ LOGFONT font_info;
+ GetObject(derived_font.GetNativeFont(), sizeof(LOGFONT), &font_info);
+ EXPECT_EQ(-5, font_info.lfHeight);
+}
+
+TEST_F(FontTest, DeriveFontKeepsOriginalSizeIfHeightOk) {
+ // This creates font of height -8.
+ Font cf(L"Arial", 6);
+ // The minimum font size is set to 5 in browser_main.cc.
+ ScopedMinimumFontSizeCallback minimum_size(5);
+
+ Font derived_font = cf.DeriveFont(-2);
+ LOGFONT font_info;
+ GetObject(derived_font.GetNativeFont(), sizeof(LOGFONT), &font_info);
+ EXPECT_EQ(-6, font_info.lfHeight);
+}
+#endif
+} // namespace
diff --git a/ui/gfx/gdi_util.cc b/ui/gfx/gdi_util.cc
new file mode 100644
index 0000000..5dbb5b5
--- /dev/null
+++ b/ui/gfx/gdi_util.cc
@@ -0,0 +1,79 @@
+// 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/gdi_util.h"
+
+namespace gfx {
+
+void CreateBitmapHeader(int width, int height, BITMAPINFOHEADER* hdr) {
+ CreateBitmapHeaderWithColorDepth(width, height, 32, hdr);
+}
+
+void CreateBitmapHeaderWithColorDepth(int width, int height, int color_depth,
+ BITMAPINFOHEADER* hdr) {
+ // These values are shared with gfx::PlatformDevice
+ hdr->biSize = sizeof(BITMAPINFOHEADER);
+ hdr->biWidth = width;
+ hdr->biHeight = -height; // minus means top-down bitmap
+ hdr->biPlanes = 1;
+ hdr->biBitCount = color_depth;
+ hdr->biCompression = BI_RGB; // no compression
+ hdr->biSizeImage = 0;
+ hdr->biXPelsPerMeter = 1;
+ hdr->biYPelsPerMeter = 1;
+ hdr->biClrUsed = 0;
+ hdr->biClrImportant = 0;
+}
+
+
+void CreateBitmapV4Header(int width, int height, BITMAPV4HEADER* hdr) {
+ // Because bmp v4 header is just an extension, we just create a v3 header and
+ // copy the bits over to the v4 header.
+ BITMAPINFOHEADER header_v3;
+ CreateBitmapHeader(width, height, &header_v3);
+ memset(hdr, 0, sizeof(BITMAPV4HEADER));
+ memcpy(hdr, &header_v3, sizeof(BITMAPINFOHEADER));
+
+ // Correct the size of the header and fill in the mask values.
+ hdr->bV4Size = sizeof(BITMAPV4HEADER);
+ hdr->bV4RedMask = 0x00ff0000;
+ hdr->bV4GreenMask = 0x0000ff00;
+ hdr->bV4BlueMask = 0x000000ff;
+ hdr->bV4AlphaMask = 0xff000000;
+}
+
+// Creates a monochrome bitmap header.
+void CreateMonochromeBitmapHeader(int width,
+ int height,
+ BITMAPINFOHEADER* hdr) {
+ hdr->biSize = sizeof(BITMAPINFOHEADER);
+ hdr->biWidth = width;
+ hdr->biHeight = -height;
+ hdr->biPlanes = 1;
+ hdr->biBitCount = 1;
+ hdr->biCompression = BI_RGB;
+ hdr->biSizeImage = 0;
+ hdr->biXPelsPerMeter = 1;
+ hdr->biYPelsPerMeter = 1;
+ hdr->biClrUsed = 0;
+ hdr->biClrImportant = 0;
+}
+
+void SubtractRectanglesFromRegion(HRGN hrgn,
+ const std::vector<gfx::Rect>& cutouts) {
+ if (cutouts.size()) {
+ HRGN cutout = ::CreateRectRgn(0, 0, 0, 0);
+ for (size_t i = 0; i < cutouts.size(); i++) {
+ ::SetRectRgn(cutout,
+ cutouts[i].x(),
+ cutouts[i].y(),
+ cutouts[i].right(),
+ cutouts[i].bottom());
+ ::CombineRgn(hrgn, hrgn, cutout, RGN_DIFF);
+ }
+ ::DeleteObject(cutout);
+ }
+}
+
+} // namespace gfx
diff --git a/ui/gfx/gdi_util.h b/ui/gfx/gdi_util.h
new file mode 100644
index 0000000..e79a34f
--- /dev/null
+++ b/ui/gfx/gdi_util.h
@@ -0,0 +1,38 @@
+// 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 UI_GFX_GDI_UTIL_H_
+#define UI_GFX_GDI_UTIL_H_
+#pragma once
+
+#include <vector>
+#include <windows.h>
+
+#include "gfx/rect.h"
+
+namespace gfx {
+
+// Creates a BITMAPINFOHEADER structure given the bitmap's size.
+void CreateBitmapHeader(int width, int height, BITMAPINFOHEADER* hdr);
+
+// Creates a BITMAPINFOHEADER structure given the bitmap's size and
+// color depth in bits per pixel.
+void CreateBitmapHeaderWithColorDepth(int width, int height, int color_depth,
+ BITMAPINFOHEADER* hdr);
+
+// Creates a BITMAPV4HEADER structure given the bitmap's size. You probably
+// only need to use BMP V4 if you need transparency (alpha channel). This
+// function sets the AlphaMask to 0xff000000.
+void CreateBitmapV4Header(int width, int height, BITMAPV4HEADER* hdr);
+
+// Creates a monochrome bitmap header.
+void CreateMonochromeBitmapHeader(int width, int height, BITMAPINFOHEADER* hdr);
+
+// Modify the given hrgn by subtracting the given rectangles.
+void SubtractRectanglesFromRegion(HRGN hrgn,
+ const std::vector<gfx::Rect>& cutouts);
+
+} // namespace gfx
+
+#endif // UI_GFX_GDI_UTIL_H_
diff --git a/ui/gfx/gfx.gyp b/ui/gfx/gfx.gyp
new file mode 100644
index 0000000..04df70d
--- /dev/null
+++ b/ui/gfx/gfx.gyp
@@ -0,0 +1,231 @@
+# 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.
+
+{
+ 'variables': {
+ 'chromium_code': 1,
+ 'grit_info_cmd': ['python', '../../tools/grit/grit_info.py',
+ '<@(grit_defines)'],
+ 'grit_cmd': ['python', '../../tools/grit/grit.py'],
+ 'grit_out_dir': '<(SHARED_INTERMEDIATE_DIR)/ui/gfx',
+ },
+ 'targets': [
+ {
+ 'target_name': 'gfx_unittests',
+ 'type': 'executable',
+ 'msvs_guid': 'C8BD2821-EAE5-4AC6-A0E4-F82CAC2956CC',
+ 'dependencies': [
+ 'gfx',
+ 'gfx_resources',
+ '../../base/base.gyp:test_support_base',
+ '../../skia/skia.gyp:skia',
+ '../../testing/gtest.gyp:gtest',
+ ],
+ 'sources': [
+ 'blit_unittest.cc',
+ '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',
+ 'scoped_image_unittest.cc',
+ 'skbitmap_operations_unittest.cc',
+ 'test_suite.cc',
+ 'test_suite.h',
+ '<(SHARED_INTERMEDIATE_DIR)/ui/gfx/gfx_resources.rc',
+ ],
+ 'include_dirs': [
+ '../..',
+ ],
+ 'conditions': [
+ ['OS=="win"', {
+ 'sources': [
+ # TODO(brettw) re-enable this when the dependencies on WindowImpl are fixed!
+ 'canvas_direct2d_unittest.cc',
+ 'icon_util_unittest.cc',
+ 'native_theme_win_unittest.cc',
+ ],
+ 'include_dirs': [
+ '../..',
+ '<(DEPTH)/third_party/wtl/include',
+ ],
+ 'msvs_settings': {
+ 'VCLinkerTool': {
+ 'DelayLoadDLLs': [
+ 'd2d1.dll',
+ 'd3d10_1.dll',
+ ],
+ 'AdditionalDependencies': [
+ 'd2d1.lib',
+ 'd3d10_1.lib',
+ ],
+ },
+ }
+ }],
+ ['OS=="linux" or OS=="freebsd" or OS=="openbsd"', {
+ 'dependencies': [
+ '../../build/linux/system.gyp:gtk',
+ ],
+ }],
+ ],
+ },
+ {
+ 'target_name': 'gfx',
+ 'type': '<(library)',
+ 'msvs_guid': '13A8D36C-0467-4B4E-BAA3-FD69C45F076A',
+ 'dependencies': [
+ '../../base/base.gyp:base',
+ '../../base/base.gyp:base_i18n',
+ '../../skia/skia.gyp:skia',
+ '../../third_party/icu/icu.gyp:icui18n',
+ '../../third_party/icu/icu.gyp:icuuc',
+ '../../third_party/libpng/libpng.gyp:libpng',
+ '../../third_party/zlib/zlib.gyp:zlib',
+ 'gfx_resources',
+ '<(libjpeg_gyp_path):libjpeg',
+ ],
+ 'sources': [
+ 'blit.cc',
+ 'blit.h',
+ 'brush.h',
+ 'canvas.cc',
+ 'canvas.h',
+ 'canvas_skia.h',
+ 'canvas_skia.cc',
+ 'canvas_skia_linux.cc',
+ 'canvas_skia_mac.mm',
+ 'canvas_skia_paint.h',
+ 'canvas_skia_win.cc',
+ 'codec/jpeg_codec.cc',
+ 'codec/jpeg_codec.h',
+ 'codec/png_codec.cc',
+ 'codec/png_codec.h',
+ 'color_utils.cc',
+ 'color_utils.h',
+ 'favicon_size.h',
+ 'font.h',
+ 'font.cc',
+ 'gfx_paths.cc',
+ 'gfx_paths.h',
+ 'gfx_module.cc',
+ 'gfx_module.h',
+ 'insets.cc',
+ 'insets.h',
+ 'native_widget_types.h',
+ 'path.cc',
+ 'path.h',
+ 'path_gtk.cc',
+ 'path_win.cc',
+ 'platform_font.h',
+ 'platform_font_gtk.h',
+ 'platform_font_gtk.cc',
+ 'platform_font_mac.h',
+ 'platform_font_mac.mm',
+ 'platform_font_win.h',
+ 'platform_font_win.cc',
+ 'point.cc',
+ 'point.h',
+ 'rect.cc',
+ 'rect.h',
+ 'scoped_cg_context_state_mac.h',
+ 'scoped_image.h',
+ 'scrollbar_size.cc',
+ 'scrollbar_size.h',
+ 'size.cc',
+ 'size.h',
+ 'skbitmap_operations.cc',
+ 'skbitmap_operations.h',
+ 'skia_util.cc',
+ 'skia_util.h',
+ 'skia_utils_gtk.cc',
+ 'skia_utils_gtk.h',
+ ],
+ 'conditions': [
+ ['OS=="win"', {
+ 'sources': [
+ 'canvas_direct2d.cc',
+ 'canvas_direct2d.h',
+ 'gdi_util.cc',
+ 'gdi_util.h',
+ 'icon_util.cc',
+ 'icon_util.h',
+ 'native_theme_win.cc',
+ 'native_theme_win.h',
+ 'win_util.cc',
+ 'win_util.h',
+ ],
+ 'include_dirs': [
+ '../..',
+ '<(DEPTH)/third_party/wtl/include',
+ ],
+ }],
+ ['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',
+ '../../build/linux/system.gyp:gtk',
+ ],
+ 'sources': [
+ 'gtk_native_view_id_manager.cc',
+ 'gtk_native_view_id_manager.h',
+ 'gtk_preserve_window.cc',
+ 'gtk_preserve_window.h',
+ 'gtk_util.cc',
+ 'gtk_util.h',
+ 'native_theme_linux.cc',
+ 'native_theme_linux.h',
+ 'native_widget_types_gtk.cc',
+ ],
+ }],
+ ],
+ },
+ {
+ # theme_resources also generates a .cc file, so it can't use the rules above.
+ 'target_name': 'gfx_resources',
+ 'type': 'none',
+ 'msvs_guid' : '5738AE53-E919-4987-A2EF-15FDBD8F90F6',
+ 'actions': [
+ {
+ 'action_name': 'gfx_resources',
+ 'variables': {
+ 'input_path': 'gfx_resources.grd',
+ },
+ 'inputs': [
+ '<!@(<(grit_info_cmd) --inputs <(input_path))',
+ ],
+ 'outputs': [
+ '<!@(<(grit_info_cmd) --outputs \'<(grit_out_dir)\' <(input_path))',
+ ],
+ 'action': [
+ '<@(grit_cmd)',
+ '-i', '<(input_path)', 'build',
+ '-o', '<(grit_out_dir)',
+ '<@(grit_defines)',
+ ],
+ 'message': 'Generating resources from <(input_path)',
+ },
+ ],
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ '<(grit_out_dir)',
+ ],
+ },
+ 'conditions': [
+ ['OS=="win"', {
+ 'dependencies': ['../../build/win/system.gyp:cygwin'],
+ }],
+ ],
+ },
+
+ ],
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/ui/gfx/gfx_module.cc b/ui/gfx/gfx_module.cc
new file mode 100644
index 0000000..882efad
--- /dev/null
+++ b/ui/gfx/gfx_module.cc
@@ -0,0 +1,21 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "gfx/gfx_module.h"
+
+namespace gfx {
+
+static GfxModule::ResourceProvider resource_provider = NULL;
+
+// static
+void GfxModule::SetResourceProvider(ResourceProvider func) {
+ resource_provider = func;
+}
+
+// static
+base::StringPiece GfxModule::GetResource(int key) {
+ return resource_provider ? resource_provider(key) : base::StringPiece();
+}
+
+} // namespace gfx
diff --git a/ui/gfx/gfx_module.h b/ui/gfx/gfx_module.h
new file mode 100644
index 0000000..0070eac
--- /dev/null
+++ b/ui/gfx/gfx_module.h
@@ -0,0 +1,34 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_MODULE_H_
+#define UI_GFX_MODULE_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "base/string_piece.h"
+
+namespace gfx {
+
+// Defines global initializers and associated methods for the gfx module.
+// See net/base/net_module.h for more details.
+class GfxModule {
+ public:
+ typedef base::StringPiece (*ResourceProvider)(int key);
+
+ // Set the function to call when the gfx module needs resources
+ static void SetResourceProvider(ResourceProvider func);
+
+ // Call the resource provider (if one exists) to get the specified resource.
+ // Returns an empty string if the resource does not exist or if there is no
+ // resource provider.
+ static base::StringPiece GetResource(int key);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(GfxModule);
+};
+
+} // namespace gfx
+
+#endif // UI_GFX_MODULE_H_
diff --git a/ui/gfx/gfx_paths.cc b/ui/gfx/gfx_paths.cc
new file mode 100644
index 0000000..bcb82ab
--- /dev/null
+++ b/ui/gfx/gfx_paths.cc
@@ -0,0 +1,51 @@
+// 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/gfx_paths.h"
+
+#include "base/command_line.h"
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/path_service.h"
+
+namespace gfx {
+
+bool PathProvider(int key, FilePath* result) {
+ // Assume that we will not need to create the directory if it does not exist.
+ // This flag can be set to true for the cases where we want to create it.
+ bool create_dir = false;
+
+ FilePath cur;
+ switch (key) {
+ // The following are only valid in the development environment, and
+ // will fail if executed from an installed executable (because the
+ // generated path won't exist).
+ case DIR_TEST_DATA:
+ if (!PathService::Get(base::DIR_SOURCE_ROOT, &cur))
+ return false;
+ cur = cur.Append(FILE_PATH_LITERAL("gfx"));
+ cur = cur.Append(FILE_PATH_LITERAL("test"));
+ cur = cur.Append(FILE_PATH_LITERAL("data"));
+ if (!file_util::PathExists(cur)) // we don't want to create this
+ return false;
+ break;
+ default:
+ return false;
+ }
+
+ if (create_dir && !file_util::PathExists(cur) &&
+ !file_util::CreateDirectory(cur))
+ return false;
+
+ *result = cur;
+ return true;
+}
+
+// This cannot be done as a static initializer sadly since Visual Studio will
+// eliminate this object file if there is no direct entry point into it.
+void RegisterPathProvider() {
+ PathService::RegisterProvider(PathProvider, PATH_START, PATH_END);
+}
+
+} // namespace gfx
diff --git a/ui/gfx/gfx_paths.h b/ui/gfx/gfx_paths.h
new file mode 100644
index 0000000..6be0cc3
--- /dev/null
+++ b/ui/gfx/gfx_paths.h
@@ -0,0 +1,28 @@
+// 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.
+
+#ifndef UI_GFX_GFX_PATHS_H_
+#define UI_GFX_GFX_PATHS_H_
+#pragma once
+
+// This file declares path keys for the app module. These can be used with
+// the PathService to access various special directories and files.
+
+namespace gfx {
+
+enum {
+ PATH_START = 2000,
+
+ // Valid only in development environment; TODO(darin): move these
+ DIR_TEST_DATA, // Directory where unit test data resides.
+
+ PATH_END
+};
+
+// Call once to register the provider for the path keys defined above.
+void RegisterPathProvider();
+
+} // namespace gfx
+
+#endif // UI_GFX_GFX_PATHS_H_
diff --git a/ui/gfx/gfx_resources.grd b/ui/gfx/gfx_resources.grd
new file mode 100644
index 0000000..8b9b4b4
--- /dev/null
+++ b/ui/gfx/gfx_resources.grd
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grit latest_public_release="0" current_release="1">
+ <outputs>
+ <output filename="grit/gfx_resources.h" type="rc_header">
+ <emit emit_type='prepend'></emit>
+ </output>
+ <output filename="grit/gfx_resources_map.cc" type="resource_map_source" />
+ <output filename="grit/gfx_resources_map.h" type="resource_map_header" />
+ <output filename="gfx_resources.pak" type="data_package" />
+ <!-- TODO(sail): remove this file once WebKit has been updated. -->
+ <output filename="../../gfx/gfx_resources.pak" type="data_package" />
+ <output filename="gfx_resources.rc" type="rc_all" />
+ </outputs>
+ <release seq="1">
+ <includes>
+ <if expr="os.find('win') != -1">
+ <!-- IDR_BITMAP_BRUSH_IMAGE is for canvas_direct2d_unittest on win -->
+ <include name="IDR_BITMAP_BRUSH_IMAGE" file="resources\bitmap_brush_image.png" type="BINDATA" />
+ </if>
+
+ <if expr="os == 'linux2' or os.find('bsd') != -1 or os == 'sunos5'">
+ <include name="IDR_LINUX_CHECKBOX_DISABLED_INDETERMINATE" file="resources\linux-checkbox-disabled-indeterminate.png" type="BINDATA" />
+ <include name="IDR_LINUX_CHECKBOX_DISABLED_OFF" file="resources\linux-checkbox-disabled-off.png" type="BINDATA" />
+ <include name="IDR_LINUX_CHECKBOX_DISABLED_ON" file="resources\linux-checkbox-disabled-on.png" type="BINDATA" />
+ <include name="IDR_LINUX_CHECKBOX_INDETERMINATE" file="resources\linux-checkbox-indeterminate.png" type="BINDATA" />
+ <include name="IDR_LINUX_CHECKBOX_OFF" file="resources\linux-checkbox-off.png" type="BINDATA" />
+ <include name="IDR_LINUX_CHECKBOX_ON" file="resources\linux-checkbox-on.png" type="BINDATA" />
+ <include name="IDR_LINUX_RADIO_DISABLED_OFF" file="resources\linux-radio-disabled-off.png" type="BINDATA" />
+ <include name="IDR_LINUX_RADIO_DISABLED_ON" file="resources\linux-radio-disabled-on.png" type="BINDATA" />
+ <include name="IDR_LINUX_RADIO_OFF" file="resources\linux-radio-off.png" type="BINDATA" />
+ <include name="IDR_LINUX_RADIO_ON" file="resources\linux-radio-on.png" type="BINDATA" />
+ <include name="IDR_PROGRESS_BAR" file="resources\linux-progress-bar.png" type="BINDATA" />
+ <include name="IDR_PROGRESS_BORDER_LEFT" file="resources\linux-progress-border-left.png" type="BINDATA" />
+ <include name="IDR_PROGRESS_BORDER_RIGHT" file="resources\linux-progress-border-right.png" type="BINDATA" />
+ <include name="IDR_PROGRESS_VALUE" file="resources\linux-progress-value.png" type="BINDATA" />
+ </if>
+ </includes>
+ </release>
+</grit>
+
diff --git a/ui/gfx/gtk_native_view_id_manager.cc b/ui/gfx/gtk_native_view_id_manager.cc
new file mode 100644
index 0000000..e9e72f2
--- /dev/null
+++ b/ui/gfx/gtk_native_view_id_manager.cc
@@ -0,0 +1,223 @@
+// 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/gtk_native_view_id_manager.h"
+
+#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
+
+#include "base/logging.h"
+#include "base/rand_util.h"
+#include "gfx/gtk_preserve_window.h"
+
+// -----------------------------------------------------------------------------
+// Bounce functions for GTK to callback into a C++ object...
+
+void OnRealize(gfx::NativeView widget, void* arg) {
+ GtkNativeViewManager* manager = reinterpret_cast<GtkNativeViewManager*>(arg);
+ manager->OnRealize(widget);
+}
+
+void OnUnrealize(gfx::NativeView widget, void *arg) {
+ GtkNativeViewManager* manager = reinterpret_cast<GtkNativeViewManager*>(arg);
+ manager->OnUnrealize(widget);
+}
+
+static void OnDestroy(GtkObject* obj, void* arg) {
+ GtkNativeViewManager* manager = reinterpret_cast<GtkNativeViewManager*>(arg);
+ manager->OnDestroy(reinterpret_cast<GtkWidget*>(obj));
+}
+
+// -----------------------------------------------------------------------------
+
+
+// -----------------------------------------------------------------------------
+// Public functions...
+
+GtkNativeViewManager::GtkNativeViewManager() {
+}
+
+GtkNativeViewManager::~GtkNativeViewManager() {
+}
+
+// static
+GtkNativeViewManager* GtkNativeViewManager::GetInstance() {
+ return Singleton<GtkNativeViewManager>::get();
+}
+
+gfx::NativeViewId GtkNativeViewManager::GetIdForWidget(gfx::NativeView widget) {
+ // This is just for unit tests:
+ if (!widget)
+ return 0;
+
+ base::AutoLock locked(lock_);
+
+ std::map<gfx::NativeView, gfx::NativeViewId>::const_iterator i =
+ native_view_to_id_.find(widget);
+
+ if (i != native_view_to_id_.end())
+ return i->second;
+
+ gfx::NativeViewId new_id =
+ static_cast<gfx::NativeViewId>(base::RandUint64());
+ while (id_to_info_.find(new_id) != id_to_info_.end())
+ new_id = static_cast<gfx::NativeViewId>(base::RandUint64());
+
+ NativeViewInfo info;
+ info.widget = widget;
+ if (GTK_WIDGET_REALIZED(widget)) {
+ GdkWindow *gdk_window = widget->window;
+ DCHECK(gdk_window);
+ info.x_window_id = GDK_WINDOW_XID(gdk_window);
+ }
+
+ native_view_to_id_[widget] = new_id;
+ id_to_info_[new_id] = info;
+
+ g_signal_connect(widget, "realize", G_CALLBACK(::OnRealize), this);
+ g_signal_connect(widget, "unrealize", G_CALLBACK(::OnUnrealize), this);
+ g_signal_connect(widget, "destroy", G_CALLBACK(::OnDestroy), this);
+
+ return new_id;
+}
+
+bool GtkNativeViewManager::GetXIDForId(XID* output, gfx::NativeViewId id) {
+ base::AutoLock locked(lock_);
+
+ std::map<gfx::NativeViewId, NativeViewInfo>::const_iterator i =
+ id_to_info_.find(id);
+
+ if (i == id_to_info_.end())
+ return false;
+
+ *output = i->second.x_window_id;
+ return true;
+}
+
+bool GtkNativeViewManager::GetPermanentXIDForId(XID* output,
+ gfx::NativeViewId id) {
+ base::AutoLock locked(lock_);
+
+ std::map<gfx::NativeViewId, NativeViewInfo>::iterator i =
+ id_to_info_.find(id);
+
+ if (i == id_to_info_.end())
+ return false;
+
+ // We only return permanent XIDs for widgets that allow us to guarantee that
+ // the XID will not change.
+ DCHECK(GTK_IS_PRESERVE_WINDOW(i->second.widget));
+ GtkPreserveWindow* widget =
+ reinterpret_cast<GtkPreserveWindow*>(i->second.widget);
+ gtk_preserve_window_set_preserve(widget, TRUE);
+
+ *output = GDK_WINDOW_XID(i->second.widget->window);
+
+ // Update the reference count on the permanent XID.
+ PermanentXIDInfo info;
+ info.widget = widget;
+ info.ref_count = 1;
+ std::pair<std::map<XID, PermanentXIDInfo>::iterator, bool> ret =
+ perm_xid_to_info_.insert(std::make_pair(*output, info));
+
+ if (!ret.second) {
+ DCHECK(ret.first->second.widget == widget);
+ ret.first->second.ref_count++;
+ }
+
+ return true;
+}
+
+void GtkNativeViewManager::ReleasePermanentXID(XID xid) {
+ base::AutoLock locked(lock_);
+
+ std::map<XID, PermanentXIDInfo>::iterator i =
+ perm_xid_to_info_.find(xid);
+
+ if (i == perm_xid_to_info_.end())
+ return;
+
+ if (i->second.ref_count > 1) {
+ i->second.ref_count--;
+ } else {
+ if (i->second.widget) {
+ gtk_preserve_window_set_preserve(i->second.widget, FALSE);
+ } else {
+ GdkWindow* window = reinterpret_cast<GdkWindow*>(
+ gdk_xid_table_lookup(xid));
+ DCHECK(window);
+ gdk_window_destroy(window);
+ }
+ perm_xid_to_info_.erase(i);
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+
+// -----------------------------------------------------------------------------
+// Private functions...
+
+gfx::NativeViewId GtkNativeViewManager::GetWidgetId(gfx::NativeView widget) {
+ lock_.AssertAcquired();
+
+ std::map<gfx::NativeView, gfx::NativeViewId>::const_iterator i =
+ native_view_to_id_.find(widget);
+
+ CHECK(i != native_view_to_id_.end());
+ return i->second;
+}
+
+void GtkNativeViewManager::OnRealize(gfx::NativeView widget) {
+ base::AutoLock locked(lock_);
+
+ const gfx::NativeViewId id = GetWidgetId(widget);
+ std::map<gfx::NativeViewId, NativeViewInfo>::iterator i =
+ id_to_info_.find(id);
+
+ CHECK(i != id_to_info_.end());
+ CHECK(widget->window);
+
+ i->second.x_window_id = GDK_WINDOW_XID(widget->window);
+}
+
+void GtkNativeViewManager::OnUnrealize(gfx::NativeView widget) {
+ base::AutoLock unrealize_locked(unrealize_lock_);
+ base::AutoLock locked(lock_);
+
+ const gfx::NativeViewId id = GetWidgetId(widget);
+ std::map<gfx::NativeViewId, NativeViewInfo>::iterator i =
+ id_to_info_.find(id);
+
+ CHECK(i != id_to_info_.end());
+}
+
+void GtkNativeViewManager::OnDestroy(gfx::NativeView widget) {
+ base::AutoLock locked(lock_);
+
+ std::map<gfx::NativeView, gfx::NativeViewId>::iterator i =
+ native_view_to_id_.find(widget);
+ CHECK(i != native_view_to_id_.end());
+
+ std::map<gfx::NativeViewId, NativeViewInfo>::iterator j =
+ id_to_info_.find(i->second);
+ CHECK(j != id_to_info_.end());
+
+ // If the XID is supposed to outlive the widget, mark it
+ // in the lookup table.
+ if (GTK_IS_PRESERVE_WINDOW(widget) &&
+ gtk_preserve_window_get_preserve(
+ reinterpret_cast<GtkPreserveWindow*>(widget))) {
+ std::map<XID, PermanentXIDInfo>::iterator k =
+ perm_xid_to_info_.find(GDK_WINDOW_XID(widget->window));
+
+ if (k != perm_xid_to_info_.end())
+ k->second.widget = NULL;
+ }
+
+ native_view_to_id_.erase(i);
+ id_to_info_.erase(j);
+}
+
+// -----------------------------------------------------------------------------
diff --git a/ui/gfx/gtk_native_view_id_manager.h b/ui/gfx/gtk_native_view_id_manager.h
new file mode 100644
index 0000000..6befcfc
--- /dev/null
+++ b/ui/gfx/gtk_native_view_id_manager.h
@@ -0,0 +1,141 @@
+// 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 UI_GFX_GTK_NATIVE_VIEW_ID_MANAGER_H_
+#define UI_GFX_GTK_NATIVE_VIEW_ID_MANAGER_H_
+#pragma once
+
+#include <map>
+
+#include "base/singleton.h"
+#include "base/synchronization/lock.h"
+#include "gfx/native_widget_types.h"
+
+typedef unsigned long XID;
+struct _GtkPreserveWindow;
+
+// NativeViewIds are the opaque values which the renderer holds as a reference
+// to a window. These ids are often used in sync calls from the renderer and
+// one cannot terminate sync calls on the UI thread as that can lead to
+// deadlocks.
+//
+// Because of this, we have the BACKGROUND_X11 thread for these calls and this
+// thread has a separate X connection in order to answer them. But one cannot
+// use GTK on multiple threads, so the BACKGROUND_X11 thread deals only in Xlib
+// calls and, thus, XIDs.
+//
+// So we could make NativeViewIds be the X id of the window. However, at the
+// time when we need to tell the renderer about its NativeViewId, an XID isn't
+// availible and it goes very much against the grain of the code to make it so.
+// Also, we worry that GTK might choose to change the underlying X window id
+// when, say, the widget is hidden or repacked. Finally, if we used XIDs then a
+// compromised renderer could start asking questions about any X windows on the
+// system.
+//
+// Thus, we have this object. It produces random NativeViewIds from GtkWidget
+// pointers and observes the various signals from the widget for when an X
+// window is created, destroyed etc. Thus it provides a thread safe mapping
+// from NativeViewIds to the current XID for that widget.
+class GtkNativeViewManager {
+ public:
+ // Returns the singleton instance.
+ static GtkNativeViewManager* GetInstance();
+
+ // Must be called from the UI thread:
+ //
+ // Return a NativeViewId for the given widget and attach to the various
+ // signals emitted by that widget. The NativeViewId is pseudo-randomly
+ // allocated so that a compromised renderer trying to guess values will fail
+ // with high probability. The NativeViewId will not be reused for the
+ // lifetime of the GtkWidget.
+ gfx::NativeViewId GetIdForWidget(gfx::NativeView widget);
+
+ // May be called from any thread:
+ //
+ // xid: (output) the resulting X window ID, or 0
+ // id: a value previously returned from GetIdForWidget
+ // returns: true if |id| is a valid id, false otherwise.
+ //
+ // If the widget referenced by |id| does not current have an X window id,
+ // |*xid| is set to 0.
+ bool GetXIDForId(XID* xid, gfx::NativeViewId id);
+
+ // Must be called from the UI thread because we may need the associated
+ // widget to create a window.
+ //
+ // Keeping the XID permanent requires a bit of overhead, so it must
+ // be explicitly requested.
+ //
+ // xid: (output) the resulting X window
+ // id: a value previously returned from GetIdForWidget
+ // returns: true if |id| is a valid id, false otherwise.
+ bool GetPermanentXIDForId(XID* xid, gfx::NativeViewId id);
+
+ // Must be called from the UI thread because we may need to access a
+ // GtkWidget or destroy a GdkWindow.
+ //
+ // If the widget associated with the XID is still alive, allow the widget
+ // to destroy the associated XID when it wants. Otherwise, destroy the
+ // GdkWindow associated with the XID.
+ void ReleasePermanentXID(XID xid);
+
+ // These are actually private functions, but need to be called from statics.
+ void OnRealize(gfx::NativeView widget);
+ void OnUnrealize(gfx::NativeView widget);
+ void OnDestroy(gfx::NativeView widget);
+
+ base::Lock& unrealize_lock() { return unrealize_lock_; }
+
+ private:
+ // This object is a singleton:
+ GtkNativeViewManager();
+ ~GtkNativeViewManager();
+ friend struct DefaultSingletonTraits<GtkNativeViewManager>;
+
+ struct NativeViewInfo {
+ NativeViewInfo() : widget(NULL), x_window_id(0) {
+ }
+ gfx::NativeView widget;
+ XID x_window_id;
+ };
+
+ gfx::NativeViewId GetWidgetId(gfx::NativeView id);
+
+ // This lock can be used to block GTK from unrealizing windows. This is needed
+ // when the BACKGROUND_X11 thread is using a window obtained via GetXIDForId,
+ // and can't allow the X11 resource to be deleted.
+ base::Lock unrealize_lock_;
+
+ // protects native_view_to_id_ and id_to_info_
+ base::Lock lock_;
+
+ // If asked for an id for the same widget twice, we want to return the same
+ // id. So this records the current mapping.
+ std::map<gfx::NativeView, gfx::NativeViewId> native_view_to_id_;
+ std::map<gfx::NativeViewId, NativeViewInfo> id_to_info_;
+
+ struct PermanentXIDInfo {
+ PermanentXIDInfo() : widget(NULL), ref_count(0) {
+ }
+ _GtkPreserveWindow* widget;
+ int ref_count;
+ };
+
+ // Used to maintain the reference count for permanent XIDs
+ // (referenced by GetPermanentXIDForId and dereferenced by
+ // ReleasePermanentXID). Only those XIDs with a positive reference count
+ // will be in the table.
+ //
+ // In general, several GTK widgets may share the same X window. We assume
+ // that is not true of the widgets stored in this registry.
+ //
+ // An XID will map to NULL, if there is an outstanding reference but the
+ // widget was destroyed. In this case, the destruction of the X window
+ // is deferred to the dropping of all references.
+ std::map<XID, PermanentXIDInfo> perm_xid_to_info_;
+
+ DISALLOW_COPY_AND_ASSIGN(GtkNativeViewManager);
+};
+
+#endif // UI_GFX_GTK_NATIVE_VIEW_ID_MANAGER_H_
diff --git a/ui/gfx/gtk_preserve_window.cc b/ui/gfx/gtk_preserve_window.cc
new file mode 100644
index 0000000..20215d1
--- /dev/null
+++ b/ui/gfx/gtk_preserve_window.cc
@@ -0,0 +1,200 @@
+// 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/gtk_preserve_window.h"
+
+#include <gdk/gdkwindow.h>
+#include <gtk/gtk.h>
+#include <gtk/gtkwidget.h>
+#include <gtk/gtkfixed.h>
+
+G_BEGIN_DECLS
+
+#define GTK_PRESERVE_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
+ GTK_TYPE_PRESERVE_WINDOW, \
+ GtkPreserveWindowPrivate))
+
+typedef struct _GtkPreserveWindowPrivate GtkPreserveWindowPrivate;
+
+struct _GtkPreserveWindowPrivate {
+ // If true, don't create/destroy windows on realize/unrealize.
+ gboolean preserve_window;
+
+ // Whether or not we delegate the resize of the GdkWindow
+ // to someone else.
+ gboolean delegate_resize;
+};
+
+G_DEFINE_TYPE(GtkPreserveWindow, gtk_preserve_window, GTK_TYPE_FIXED)
+
+static void gtk_preserve_window_destroy(GtkObject* object);
+static void gtk_preserve_window_realize(GtkWidget* widget);
+static void gtk_preserve_window_unrealize(GtkWidget* widget);
+static void gtk_preserve_window_size_allocate(GtkWidget* widget,
+ GtkAllocation* allocation);
+
+static void gtk_preserve_window_class_init(GtkPreserveWindowClass *klass) {
+ GtkWidgetClass* widget_class = reinterpret_cast<GtkWidgetClass*>(klass);
+ widget_class->realize = gtk_preserve_window_realize;
+ widget_class->unrealize = gtk_preserve_window_unrealize;
+ widget_class->size_allocate = gtk_preserve_window_size_allocate;
+
+ GtkObjectClass* object_class = reinterpret_cast<GtkObjectClass*>(klass);
+ object_class->destroy = gtk_preserve_window_destroy;
+
+ GObjectClass* gobject_class = G_OBJECT_CLASS(klass);
+ g_type_class_add_private(gobject_class, sizeof(GtkPreserveWindowPrivate));
+}
+
+static void gtk_preserve_window_init(GtkPreserveWindow* widget) {
+ GtkPreserveWindowPrivate* priv = GTK_PRESERVE_WINDOW_GET_PRIVATE(widget);
+ priv->preserve_window = FALSE;
+
+ // These widgets always have their own window.
+ gtk_fixed_set_has_window(GTK_FIXED(widget), TRUE);
+}
+
+GtkWidget* gtk_preserve_window_new() {
+ return GTK_WIDGET(g_object_new(GTK_TYPE_PRESERVE_WINDOW, NULL));
+}
+
+static void gtk_preserve_window_destroy(GtkObject* object) {
+ GtkWidget* widget = reinterpret_cast<GtkWidget*>(object);
+ GtkPreserveWindowPrivate* priv = GTK_PRESERVE_WINDOW_GET_PRIVATE(widget);
+
+ if (widget->window) {
+ gdk_window_set_user_data(widget->window, NULL);
+ // If the window is preserved, someone else must destroy it.
+ if (!priv->preserve_window)
+ gdk_window_destroy(widget->window);
+ widget->window = NULL;
+ }
+
+ GTK_OBJECT_CLASS(gtk_preserve_window_parent_class)->destroy(object);
+}
+
+static void gtk_preserve_window_realize(GtkWidget* widget) {
+ g_return_if_fail(GTK_IS_PRESERVE_WINDOW(widget));
+
+ if (widget->window) {
+ gdk_window_reparent(widget->window,
+ gtk_widget_get_parent_window(widget),
+ widget->allocation.x,
+ widget->allocation.y);
+ widget->style = gtk_style_attach(widget->style, widget->window);
+ gtk_style_set_background(widget->style, widget->window, GTK_STATE_NORMAL);
+
+ gint event_mask = gtk_widget_get_events(widget);
+ event_mask |= GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK;
+ gdk_window_set_events(widget->window, (GdkEventMask) event_mask);
+ gdk_window_set_user_data(widget->window, widget);
+
+ // Deprecated as of GTK 2.22. Used for compatibility.
+ // It should be: gtk_widget_set_realized(widget, TRUE)
+ GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
+ } else {
+ GTK_WIDGET_CLASS(gtk_preserve_window_parent_class)->realize(widget);
+ }
+}
+
+static void gtk_preserve_window_unrealize(GtkWidget* widget) {
+ g_return_if_fail(GTK_IS_PRESERVE_WINDOW(widget));
+
+ GtkPreserveWindowPrivate* priv = GTK_PRESERVE_WINDOW_GET_PRIVATE(widget);
+ if (priv->preserve_window) {
+ GtkWidgetClass* widget_class =
+ GTK_WIDGET_CLASS(gtk_preserve_window_parent_class);
+ GtkContainerClass* container_class =
+ GTK_CONTAINER_CLASS(gtk_preserve_window_parent_class);
+
+ // Deprecated as of GTK 2.22. Used for compatibility.
+ // It should be: gtk_widget_get_mapped()
+ if (GTK_WIDGET_MAPPED(widget)) {
+ widget_class->unmap(widget);
+
+ // Deprecated as of GTK 2.22. Used for compatibility.
+ // It should be: gtk_widget_set_mapped(widget, FALSE)
+ GTK_WIDGET_UNSET_FLAGS(widget, GTK_MAPPED);
+ }
+
+ // This is the behavior from GtkWidget, inherited by GtkFixed.
+ // It is unclear why we should not call the potentially overridden
+ // unrealize method (via the callback), but doing so causes errors.
+ container_class->forall(
+ GTK_CONTAINER(widget), FALSE,
+ reinterpret_cast<GtkCallback>(gtk_widget_unrealize), NULL);
+
+ gtk_style_detach(widget->style);
+ gdk_window_reparent(widget->window, gdk_get_default_root_window(), 0, 0);
+ gtk_selection_remove_all(widget);
+ gdk_window_set_user_data(widget->window, NULL);
+
+ // Deprecated as of GTK 2.22. Used for compatibility.
+ // It should be: gtk_widget_set_realized(widget, FALSE)
+ GTK_WIDGET_UNSET_FLAGS(widget, GTK_REALIZED);
+ } else {
+ GTK_WIDGET_CLASS(gtk_preserve_window_parent_class)->unrealize(widget);
+ }
+}
+
+gboolean gtk_preserve_window_get_preserve(GtkPreserveWindow* window) {
+ g_return_val_if_fail(GTK_IS_PRESERVE_WINDOW(window), FALSE);
+ GtkPreserveWindowPrivate* priv = GTK_PRESERVE_WINDOW_GET_PRIVATE(window);
+
+ return priv->preserve_window;
+}
+
+void gtk_preserve_window_set_preserve(GtkPreserveWindow* window,
+ gboolean value) {
+ g_return_if_fail(GTK_IS_PRESERVE_WINDOW(window));
+ GtkPreserveWindowPrivate* priv = GTK_PRESERVE_WINDOW_GET_PRIVATE(window);
+ priv->preserve_window = value;
+
+ GtkWidget* widget = GTK_WIDGET(window);
+ if (value && !widget->window) {
+ GdkWindowAttr attributes;
+ gint attributes_mask;
+
+ // We may not know the width and height, so we rely on the fact
+ // that a size-allocation will resize it later.
+ attributes.width = 1;
+ attributes.height = 1;
+
+ attributes.window_type = GDK_WINDOW_CHILD;
+ attributes.wclass = GDK_INPUT_OUTPUT;
+
+ attributes.visual = gtk_widget_get_visual(widget);
+ attributes.colormap = gtk_widget_get_colormap(widget);
+
+ attributes_mask = GDK_WA_VISUAL | GDK_WA_COLORMAP;
+ widget->window = gdk_window_new(
+ gdk_get_default_root_window(), &attributes, attributes_mask);
+ } else if (!value && widget->window && !GTK_WIDGET_REALIZED(widget)) {
+ gdk_window_destroy(widget->window);
+ widget->window = NULL;
+ }
+}
+
+void gtk_preserve_window_size_allocate(GtkWidget* widget,
+ GtkAllocation* allocation) {
+ g_return_if_fail(GTK_IS_PRESERVE_WINDOW(widget));
+ GtkPreserveWindowPrivate* priv = GTK_PRESERVE_WINDOW_GET_PRIVATE(widget);
+
+ if (priv->delegate_resize) {
+ // Just update the state. Someone else will gdk_window_resize the
+ // associated GdkWindow.
+ widget->allocation = *allocation;
+ } else {
+ GTK_WIDGET_CLASS(gtk_preserve_window_parent_class)->size_allocate(
+ widget, allocation);
+ }
+}
+
+void gtk_preserve_window_delegate_resize(GtkPreserveWindow* widget,
+ gboolean delegate) {
+ GtkPreserveWindowPrivate* priv = GTK_PRESERVE_WINDOW_GET_PRIVATE(widget);
+ priv->delegate_resize = delegate;
+}
+
+G_END_DECLS
diff --git a/ui/gfx/gtk_preserve_window.h b/ui/gfx/gtk_preserve_window.h
new file mode 100644
index 0000000..3c9b1d8
--- /dev/null
+++ b/ui/gfx/gtk_preserve_window.h
@@ -0,0 +1,64 @@
+// 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 UI_GFX_GTK_PRESERVE_WINDOW_H_
+#define UI_GFX_GTK_PRESERVE_WINDOW_H_
+#pragma once
+
+#include <gdk/gdk.h>
+#include <gtk/gtk.h>
+
+// GtkFixed creates an X window when realized and destroys an X window
+// when unrealized. GtkPreserveWindow allows overrides this
+// behaviour. When preserve is set (via gtk_preserve_window_set_preserve),
+// the X window is only destroyed when the widget is destroyed.
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_PRESERVE_WINDOW \
+ (gtk_preserve_window_get_type())
+#define GTK_PRESERVE_WINDOW(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), GTK_TYPE_PERSERVE_WINDOW, \
+ GtkPreserveWindow))
+#define GTK_PRESERVE_WINDOW_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), GTK_TYPE_PRESERVE_WINDOW, \
+ GtkPreserveWindowClass))
+#define GTK_IS_PRESERVE_WINDOW(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GTK_TYPE_PRESERVE_WINDOW))
+#define GTK_IS_PRESERVE_WINDOW_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), GTK_TYPE_PRESERVE_WINDOW))
+#define GTK_PRESERVE_WINDOW_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS((obj), GTK_TYPE_PRESERVE_WINDOW, \
+ GtkPreserveWindowClass))
+
+typedef struct _GtkPreserveWindow GtkPreserveWindow;
+typedef struct _GtkPreserveWindowClass GtkPreserveWindowClass;
+
+struct _GtkPreserveWindow {
+ // Parent class.
+ GtkFixed fixed;
+};
+
+struct _GtkPreserveWindowClass {
+ GtkFixedClass parent_class;
+};
+
+GType gtk_preserve_window_get_type() G_GNUC_CONST;
+GtkWidget* gtk_preserve_window_new();
+
+// Whether or not we should preserve associated windows as the widget
+// is realized or unrealized.
+gboolean gtk_preserve_window_get_preserve(GtkPreserveWindow* widget);
+void gtk_preserve_window_set_preserve(GtkPreserveWindow* widget,
+ gboolean value);
+
+// Whether or not someone else will gdk_window_resize the GdkWindow associated
+// with this widget (needed by the GPU process to synchronize resizing
+// with swapped between front and back buffer).
+void gtk_preserve_window_delegate_resize(GtkPreserveWindow* widget,
+ gboolean delegate);
+
+G_END_DECLS
+
+#endif // UI_GFX_GTK_PRESERVE_WINDOW_H_
diff --git a/ui/gfx/gtk_util.cc b/ui/gfx/gtk_util.cc
new file mode 100644
index 0000000..fc240e0
--- /dev/null
+++ b/ui/gfx/gtk_util.cc
@@ -0,0 +1,212 @@
+// 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/gtk_util.h"
+
+#include <gdk/gdk.h>
+#include <gtk/gtk.h>
+#include <stdlib.h>
+
+#include "base/basictypes.h"
+#include "base/command_line.h"
+#include "base/linux_util.h"
+#include "gfx/rect.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkUnPreMultiply.h"
+
+namespace {
+
+// A process wide singleton that manages our usage of gdk
+// cursors. gdk_cursor_new() hits the disk in several places and GdkCursor
+// instances can be reused throughout the process.
+class GdkCursorCache {
+ public:
+ GdkCursorCache() {}
+ ~GdkCursorCache() {
+ for (std::map<GdkCursorType, GdkCursor*>::iterator it =
+ cursor_cache_.begin(); it != cursor_cache_.end(); ++it) {
+ gdk_cursor_unref(it->second);
+ }
+ cursor_cache_.clear();
+ }
+
+ GdkCursor* GetCursorImpl(GdkCursorType type) {
+ std::map<GdkCursorType, GdkCursor*>::iterator it = cursor_cache_.find(type);
+ GdkCursor* cursor = NULL;
+ if (it == cursor_cache_.end()) {
+ cursor = gdk_cursor_new(type);
+ cursor_cache_.insert(std::make_pair(type, cursor));
+ } else {
+ cursor = it->second;
+ }
+
+ // It is not necessary to add a reference here. The callers can ref the
+ // cursor if they need it for something.
+ return cursor;
+ }
+
+ std::map<GdkCursorType, GdkCursor*> cursor_cache_;
+
+ DISALLOW_COPY_AND_ASSIGN(GdkCursorCache);
+};
+
+void FreePixels(guchar* pixels, gpointer data) {
+ free(data);
+}
+
+// Common implementation of ConvertAcceleratorsFromWindowsStyle() and
+// RemoveWindowsStyleAccelerators().
+// Replaces all ampersands (as used in our grd files to indicate mnemonics)
+// to |target|. Similarly any underscores get replaced with two underscores as
+// is needed by pango.
+std::string ConvertAmperstandsTo(const std::string& label,
+ const std::string& target) {
+ std::string ret;
+ ret.reserve(label.length() * 2);
+ for (size_t i = 0; i < label.length(); ++i) {
+ if ('_' == label[i]) {
+ ret.push_back('_');
+ ret.push_back('_');
+ } else if ('&' == label[i]) {
+ if (i + 1 < label.length() && '&' == label[i + 1]) {
+ ret.push_back('&');
+ ++i;
+ } else {
+ ret.append(target);
+ }
+ } else {
+ ret.push_back(label[i]);
+ }
+ }
+
+ return ret;
+}
+
+} // namespace
+
+namespace gfx {
+
+void GtkInitFromCommandLine(const CommandLine& command_line) {
+ const std::vector<std::string>& args = command_line.argv();
+ int argc = args.size();
+ scoped_array<char *> argv(new char *[argc + 1]);
+ for (size_t i = 0; i < args.size(); ++i) {
+ // TODO(piman@google.com): can gtk_init modify argv? Just being safe
+ // here.
+ argv[i] = strdup(args[i].c_str());
+ }
+ argv[argc] = NULL;
+ char **argv_pointer = argv.get();
+
+ gtk_init(&argc, &argv_pointer);
+ for (size_t i = 0; i < args.size(); ++i) {
+ free(argv[i]);
+ }
+}
+
+GdkPixbuf* GdkPixbufFromSkBitmap(const SkBitmap* bitmap) {
+ if (bitmap->isNull())
+ return NULL;
+
+ bitmap->lockPixels();
+
+ int width = bitmap->width();
+ int height = bitmap->height();
+ int stride = bitmap->rowBytes();
+
+ // SkBitmaps are premultiplied, we need to unpremultiply them.
+ const int kBytesPerPixel = 4;
+ uint8* divided = static_cast<uint8*>(malloc(height * stride));
+
+ for (int y = 0, i = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ uint32 pixel = bitmap->getAddr32(0, y)[x];
+
+ int alpha = SkColorGetA(pixel);
+ if (alpha != 0 && alpha != 255) {
+ SkColor unmultiplied = SkUnPreMultiply::PMColorToColor(pixel);
+ divided[i + 0] = SkColorGetR(unmultiplied);
+ divided[i + 1] = SkColorGetG(unmultiplied);
+ divided[i + 2] = SkColorGetB(unmultiplied);
+ divided[i + 3] = alpha;
+ } else {
+ divided[i + 0] = SkColorGetR(pixel);
+ divided[i + 1] = SkColorGetG(pixel);
+ divided[i + 2] = SkColorGetB(pixel);
+ divided[i + 3] = alpha;
+ }
+ i += kBytesPerPixel;
+ }
+ }
+
+ // This pixbuf takes ownership of our malloc()ed data and will
+ // free it for us when it is destroyed.
+ GdkPixbuf* pixbuf = gdk_pixbuf_new_from_data(
+ divided,
+ GDK_COLORSPACE_RGB, // The only colorspace gtk supports.
+ true, // There is an alpha channel.
+ 8,
+ width, height, stride, &FreePixels, divided);
+
+ bitmap->unlockPixels();
+ return pixbuf;
+}
+
+void SubtractRectanglesFromRegion(GdkRegion* region,
+ const std::vector<Rect>& cutouts) {
+ for (size_t i = 0; i < cutouts.size(); ++i) {
+ GdkRectangle rect = cutouts[i].ToGdkRectangle();
+ GdkRegion* rect_region = gdk_region_rectangle(&rect);
+ gdk_region_subtract(region, rect_region);
+ // TODO(deanm): It would be nice to be able to reuse the GdkRegion here.
+ gdk_region_destroy(rect_region);
+ }
+}
+
+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;
+}
+
+GdkCursor* GetCursor(int type) {
+ static GdkCursorCache impl;
+ return impl.GetCursorImpl(static_cast<GdkCursorType>(type));
+}
+
+std::string ConvertAcceleratorsFromWindowsStyle(const std::string& label) {
+ return ConvertAmperstandsTo(label, "_");
+}
+
+std::string RemoveWindowsStyleAccelerators(const std::string& label) {
+ return ConvertAmperstandsTo(label, "");
+}
+
+uint8_t* BGRAToRGBA(const uint8_t* pixels, int width, int height, int stride) {
+ if (stride == 0)
+ stride = width * 4;
+
+ uint8_t* new_pixels = static_cast<uint8_t*>(malloc(height * stride));
+
+ // We have to copy the pixels and swap from BGRA to RGBA.
+ for (int i = 0; i < height; ++i) {
+ for (int j = 0; j < width; ++j) {
+ int idx = i * stride + j * 4;
+ new_pixels[idx] = pixels[idx + 2];
+ new_pixels[idx + 1] = pixels[idx + 1];
+ new_pixels[idx + 2] = pixels[idx];
+ new_pixels[idx + 3] = pixels[idx + 3];
+ }
+ }
+
+ return new_pixels;
+}
+
+} // namespace gfx
diff --git a/ui/gfx/gtk_util.h b/ui/gfx/gtk_util.h
new file mode 100644
index 0000000..71cb792
--- /dev/null
+++ b/ui/gfx/gtk_util.h
@@ -0,0 +1,84 @@
+// 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 UI_GFX_GTK_UTIL_H_
+#define UI_GFX_GTK_UTIL_H_
+#pragma once
+
+#include <glib-object.h>
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+#include "base/scoped_ptr.h"
+
+typedef struct _GdkPixbuf GdkPixbuf;
+typedef struct _GdkRegion GdkRegion;
+typedef struct _GdkCursor GdkCursor;
+
+class CommandLine;
+class SkBitmap;
+
+namespace gfx {
+
+class Rect;
+
+// Call gtk_init() using the argc and argv from command_line.
+// gtk_init() wants an argc and argv that it can mutate; we provide those,
+// but leave the original CommandLine unaltered.
+void GtkInitFromCommandLine(const CommandLine& command_line);
+
+// Convert and copy a SkBitmap to a GdkPixbuf. NOTE: this uses BGRAToRGBA, so
+// it is an expensive operation. The returned GdkPixbuf will have a refcount of
+// 1, and the caller is responsible for unrefing it when done.
+GdkPixbuf* GdkPixbufFromSkBitmap(const SkBitmap* bitmap);
+
+// Modify the given region by subtracting the given rectangles.
+void SubtractRectanglesFromRegion(GdkRegion* region,
+ const std::vector<Rect>& cutouts);
+
+// Returns the resolution (DPI) used by pango. A negative values means the
+// resolution hasn't been set.
+double GetPangoResolution();
+
+// Returns a static instance of a GdkCursor* object, sharable across the
+// process. Caller must gdk_cursor_ref() it if they want to assume ownership.
+GdkCursor* GetCursor(int type);
+
+// Change windows accelerator style to GTK style. (GTK uses _ for
+// accelerators. Windows uses & with && as an escape for &.)
+std::string ConvertAcceleratorsFromWindowsStyle(const std::string& label);
+
+// Removes the "&" accelerators from a Windows label.
+std::string RemoveWindowsStyleAccelerators(const std::string& label);
+
+// Makes a copy of |pixels| with the ordering changed from BGRA to RGBA.
+// The caller is responsible for free()ing the data. If |stride| is 0, it's
+// assumed to be 4 * |width|.
+uint8_t* BGRAToRGBA(const uint8_t* pixels, int width, int height, int stride);
+
+} // namespace gfx
+
+namespace {
+// A helper class that will g_object_unref |p| when it goes out of scope.
+// This never adds a ref, it only unrefs.
+template <typename Type>
+struct GObjectUnrefer {
+ void operator()(Type* ptr) const {
+ if (ptr)
+ g_object_unref(ptr);
+ }
+};
+} // namespace
+
+// It's not legal C++ to have a templatized typedefs, so we wrap it in a
+// struct. When using this, you need to include ::Type. E.g.,
+// ScopedGObject<GdkPixbufLoader>::Type loader(gdk_pixbuf_loader_new());
+template<class T>
+struct ScopedGObject {
+ typedef scoped_ptr_malloc<T, GObjectUnrefer<T> > Type;
+};
+
+#endif // UI_GFX_GTK_UTIL_H_
diff --git a/ui/gfx/icon_util.cc b/ui/gfx/icon_util.cc
new file mode 100644
index 0000000..cabc505
--- /dev/null
+++ b/ui/gfx/icon_util.cc
@@ -0,0 +1,457 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "gfx/icon_util.h"
+
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/scoped_ptr.h"
+#include "base/win/scoped_handle.h"
+#include "gfx/size.h"
+#include "skia/ext/image_operations.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+
+// Defining the dimensions for the icon images. We store only one value because
+// we always resize to a square image; that is, the value 48 means that we are
+// going to resize the given bitmap to a 48 by 48 pixels bitmap.
+//
+// The icon images appear in the icon file in same order in which their
+// corresponding dimensions appear in the |icon_dimensions_| array, so it is
+// important to keep this array sorted. Also note that the maximum icon image
+// size we can handle is 255 by 255.
+const int IconUtil::icon_dimensions_[] = {
+ 8, // Recommended by the MSDN as a nice to have icon size.
+ 10, // Used by the Shell (e.g. for shortcuts).
+ 14, // Recommended by the MSDN as a nice to have icon size.
+ 16, // Toolbar, Application and Shell icon sizes.
+ 22, // Recommended by the MSDN as a nice to have icon size.
+ 24, // Used by the Shell (e.g. for shortcuts).
+ 32, // Toolbar, Dialog and Wizard icon size.
+ 40, // Quick Launch.
+ 48, // Alt+Tab icon size.
+ 64, // Recommended by the MSDN as a nice to have icon size.
+ 96, // Recommended by the MSDN as a nice to have icon size.
+ 128 // Used by the Shell (e.g. for shortcuts).
+};
+
+HICON IconUtil::CreateHICONFromSkBitmap(const SkBitmap& bitmap) {
+ // Only 32 bit ARGB bitmaps are supported. We also try to perform as many
+ // validations as we can on the bitmap.
+ SkAutoLockPixels bitmap_lock(bitmap);
+ if ((bitmap.getConfig() != SkBitmap::kARGB_8888_Config) ||
+ (bitmap.width() <= 0) || (bitmap.height() <= 0) ||
+ (bitmap.getPixels() == NULL))
+ return NULL;
+
+ // We start by creating a DIB which we'll use later on in order to create
+ // the HICON. We use BITMAPV5HEADER since the bitmap we are about to convert
+ // may contain an alpha channel and the V5 header allows us to specify the
+ // alpha mask for the DIB.
+ BITMAPV5HEADER bitmap_header;
+ InitializeBitmapHeader(&bitmap_header, bitmap.width(), bitmap.height());
+ void* bits;
+ HDC hdc = ::GetDC(NULL);
+ HBITMAP dib;
+ dib = ::CreateDIBSection(hdc, reinterpret_cast<BITMAPINFO*>(&bitmap_header),
+ DIB_RGB_COLORS, &bits, NULL, 0);
+ DCHECK(dib);
+ ::ReleaseDC(NULL, hdc);
+ memcpy(bits, bitmap.getPixels(), bitmap.width() * bitmap.height() * 4);
+
+ // Icons are generally created using an AND and XOR masks where the AND
+ // specifies boolean transparency (the pixel is either opaque or
+ // transparent) and the XOR mask contains the actual image pixels. If the XOR
+ // mask bitmap has an alpha channel, the AND monochrome bitmap won't
+ // actually be used for computing the pixel transparency. Even though all our
+ // bitmap has an alpha channel, Windows might not agree when all alpha values
+ // are zero. So the monochrome bitmap is created with all pixels transparent
+ // for this case. Otherwise, it is created with all pixels opaque.
+ bool bitmap_has_alpha_channel = PixelsHaveAlpha(
+ static_cast<const uint32*>(bitmap.getPixels()),
+ bitmap.width() * bitmap.height());
+
+ scoped_array<uint8> mask_bits;
+ if (!bitmap_has_alpha_channel) {
+ // Bytes per line with paddings to make it word alignment.
+ size_t bytes_per_line = (bitmap.width() + 0xF) / 16 * 2;
+ size_t mask_bits_size = bytes_per_line * bitmap.height();
+
+ mask_bits.reset(new uint8[mask_bits_size]);
+ DCHECK(mask_bits.get());
+
+ // Make all pixels transparent.
+ memset(mask_bits.get(), 0xFF, mask_bits_size);
+ }
+
+ HBITMAP mono_bitmap = ::CreateBitmap(bitmap.width(), bitmap.height(), 1, 1,
+ reinterpret_cast<LPVOID>(mask_bits.get()));
+ DCHECK(mono_bitmap);
+
+ ICONINFO icon_info;
+ icon_info.fIcon = TRUE;
+ icon_info.xHotspot = 0;
+ icon_info.yHotspot = 0;
+ icon_info.hbmMask = mono_bitmap;
+ icon_info.hbmColor = dib;
+ HICON icon = ::CreateIconIndirect(&icon_info);
+ ::DeleteObject(dib);
+ ::DeleteObject(mono_bitmap);
+ return icon;
+}
+
+SkBitmap* IconUtil::CreateSkBitmapFromHICON(HICON icon, const gfx::Size& s) {
+ // We start with validating parameters.
+ ICONINFO icon_info;
+ if (!icon || !(::GetIconInfo(icon, &icon_info)) ||
+ !icon_info.fIcon || s.IsEmpty())
+ return NULL;
+
+ // Allocating memory for the SkBitmap object. We are going to create an ARGB
+ // bitmap so we should set the configuration appropriately.
+ SkBitmap* bitmap = new SkBitmap;
+ DCHECK(bitmap);
+ bitmap->setConfig(SkBitmap::kARGB_8888_Config, s.width(), s.height());
+ bitmap->allocPixels();
+ bitmap->eraseARGB(0, 0, 0, 0);
+ SkAutoLockPixels bitmap_lock(*bitmap);
+
+ // Now we should create a DIB so that we can use ::DrawIconEx in order to
+ // obtain the icon's image.
+ BITMAPV5HEADER h;
+ InitializeBitmapHeader(&h, s.width(), s.height());
+ HDC dc = ::GetDC(NULL);
+ uint32* bits;
+ HBITMAP dib = ::CreateDIBSection(dc, reinterpret_cast<BITMAPINFO*>(&h),
+ DIB_RGB_COLORS, reinterpret_cast<void**>(&bits), NULL, 0);
+ DCHECK(dib);
+ HDC dib_dc = CreateCompatibleDC(dc);
+ DCHECK(dib_dc);
+ ::SelectObject(dib_dc, dib);
+
+ // Windows icons are defined using two different masks. The XOR mask, which
+ // represents the icon image and an AND mask which is a monochrome bitmap
+ // which indicates the transparency of each pixel.
+ //
+ // To make things more complex, the icon image itself can be an ARGB bitmap
+ // and therefore contain an alpha channel which specifies the transparency
+ // for each pixel. Unfortunately, there is no easy way to determine whether
+ // or not a bitmap has an alpha channel and therefore constructing the bitmap
+ // for the icon is nothing but straightforward.
+ //
+ // The idea is to read the AND mask but use it only if we know for sure that
+ // the icon image does not have an alpha channel. The only way to tell if the
+ // bitmap has an alpha channel is by looking through the pixels and checking
+ // whether there are non-zero alpha bytes.
+ //
+ // We start by drawing the AND mask into our DIB.
+ size_t num_pixels = s.GetArea();
+ memset(bits, 0, num_pixels * 4);
+ ::DrawIconEx(dib_dc, 0, 0, icon, s.width(), s.height(), 0, NULL, DI_MASK);
+
+ // Capture boolean opacity. We may not use it if we find out the bitmap has
+ // an alpha channel.
+ bool* opaque = new bool[num_pixels];
+ DCHECK(opaque);
+ for (size_t i = 0; i < num_pixels; ++i)
+ opaque[i] = !bits[i];
+
+ // Then draw the image itself which is really the XOR mask.
+ memset(bits, 0, num_pixels * 4);
+ ::DrawIconEx(dib_dc, 0, 0, icon, s.width(), s.height(), 0, NULL, DI_NORMAL);
+ memcpy(bitmap->getPixels(), static_cast<void*>(bits), num_pixels * 4);
+
+ // Finding out whether the bitmap has an alpha channel.
+ bool bitmap_has_alpha_channel = PixelsHaveAlpha(
+ static_cast<const uint32*>(bitmap->getPixels()), num_pixels);
+
+ // If the bitmap does not have an alpha channel, we need to build it using
+ // the previously captured AND mask. Otherwise, we are done.
+ if (!bitmap_has_alpha_channel) {
+ uint32* p = static_cast<uint32*>(bitmap->getPixels());
+ for (size_t i = 0; i < num_pixels; ++p, ++i) {
+ DCHECK_EQ((*p & 0xff000000), 0u);
+ if (opaque[i])
+ *p |= 0xff000000;
+ else
+ *p &= 0x00ffffff;
+ }
+ }
+
+ delete [] opaque;
+ ::DeleteDC(dib_dc);
+ ::DeleteObject(dib);
+ ::ReleaseDC(NULL, dc);
+
+ return bitmap;
+}
+
+bool IconUtil::CreateIconFileFromSkBitmap(const SkBitmap& bitmap,
+ const FilePath& icon_path) {
+ // Only 32 bit ARGB bitmaps are supported. We also make sure the bitmap has
+ // been properly initialized.
+ SkAutoLockPixels bitmap_lock(bitmap);
+ if ((bitmap.getConfig() != SkBitmap::kARGB_8888_Config) ||
+ (bitmap.height() <= 0) || (bitmap.width() <= 0) ||
+ (bitmap.getPixels() == NULL))
+ return false;
+
+ // We start by creating the file.
+ base::win::ScopedHandle icon_file(::CreateFile(icon_path.value().c_str(),
+ GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL));
+
+ if (icon_file.Get() == INVALID_HANDLE_VALUE)
+ return false;
+
+ // Creating a set of bitmaps corresponding to the icon images we'll end up
+ // storing in the icon file. Each bitmap is created by resizing the given
+ // bitmap to the desired size.
+ std::vector<SkBitmap> bitmaps;
+ CreateResizedBitmapSet(bitmap, &bitmaps);
+ DCHECK(!bitmaps.empty());
+ size_t bitmap_count = bitmaps.size();
+
+ // Computing the total size of the buffer we need in order to store the
+ // images in the desired icon format.
+ size_t buffer_size = ComputeIconFileBufferSize(bitmaps);
+ unsigned char* buffer = new unsigned char[buffer_size];
+ DCHECK(buffer != NULL);
+ memset(buffer, 0, buffer_size);
+
+ // Setting the information in the structures residing within the buffer.
+ // First, we set the information which doesn't require iterating through the
+ // bitmap set and then we set the bitmap specific structures. In the latter
+ // step we also copy the actual bits.
+ ICONDIR* icon_dir = reinterpret_cast<ICONDIR*>(buffer);
+ icon_dir->idType = kResourceTypeIcon;
+ icon_dir->idCount = bitmap_count;
+ size_t icon_dir_count = bitmap_count - 1; // Note DCHECK(!bitmaps.empty())!
+ size_t offset = sizeof(ICONDIR) + (sizeof(ICONDIRENTRY) * icon_dir_count);
+ for (size_t i = 0; i < bitmap_count; i++) {
+ ICONIMAGE* image = reinterpret_cast<ICONIMAGE*>(buffer + offset);
+ DCHECK_LT(offset, buffer_size);
+ size_t icon_image_size = 0;
+ SetSingleIconImageInformation(bitmaps[i], i, icon_dir, image, offset,
+ &icon_image_size);
+ DCHECK_GT(icon_image_size, 0U);
+ offset += icon_image_size;
+ }
+ DCHECK_EQ(offset, buffer_size);
+
+ // Finally, writing the data info the file.
+ DWORD bytes_written;
+ bool delete_file = false;
+ if (!WriteFile(icon_file.Get(), buffer, buffer_size, &bytes_written, NULL) ||
+ bytes_written != buffer_size)
+ delete_file = true;
+
+ ::CloseHandle(icon_file.Take());
+ delete [] buffer;
+ if (delete_file) {
+ bool success = file_util::Delete(icon_path, false);
+ DCHECK(success);
+ }
+
+ return !delete_file;
+}
+
+bool IconUtil::PixelsHaveAlpha(const uint32* pixels, size_t num_pixels) {
+ for (const uint32* end = pixels + num_pixels; pixels != end; ++pixels) {
+ if ((*pixels & 0xff000000) != 0)
+ return true;
+ }
+
+ return false;
+}
+
+void IconUtil::InitializeBitmapHeader(BITMAPV5HEADER* header, int width,
+ int height) {
+ DCHECK(header);
+ memset(header, 0, sizeof(BITMAPV5HEADER));
+ header->bV5Size = sizeof(BITMAPV5HEADER);
+
+ // Note that icons are created using top-down DIBs so we must negate the
+ // value used for the icon's height.
+ header->bV5Width = width;
+ header->bV5Height = -height;
+ header->bV5Planes = 1;
+ header->bV5Compression = BI_RGB;
+
+ // Initializing the bitmap format to 32 bit ARGB.
+ header->bV5BitCount = 32;
+ header->bV5RedMask = 0x00FF0000;
+ header->bV5GreenMask = 0x0000FF00;
+ header->bV5BlueMask = 0x000000FF;
+ header->bV5AlphaMask = 0xFF000000;
+
+ // Use the system color space. The default value is LCS_CALIBRATED_RGB, which
+ // causes us to crash if we don't specify the approprite gammas, etc. See
+ // <http://msdn.microsoft.com/en-us/library/ms536531(VS.85).aspx> and
+ // <http://b/1283121>.
+ header->bV5CSType = LCS_WINDOWS_COLOR_SPACE;
+
+ // Use a valid value for bV5Intent as 0 is not a valid one.
+ // <http://msdn.microsoft.com/en-us/library/dd183381(VS.85).aspx>
+ header->bV5Intent = LCS_GM_IMAGES;
+}
+
+void IconUtil::SetSingleIconImageInformation(const SkBitmap& bitmap,
+ size_t index,
+ ICONDIR* icon_dir,
+ ICONIMAGE* icon_image,
+ size_t image_offset,
+ size_t* image_byte_count) {
+ DCHECK(icon_dir != NULL);
+ DCHECK(icon_image != NULL);
+ DCHECK_GT(image_offset, 0U);
+ DCHECK(image_byte_count != NULL);
+
+ // We start by computing certain image values we'll use later on.
+ size_t xor_mask_size, bytes_in_resource;
+ ComputeBitmapSizeComponents(bitmap,
+ &xor_mask_size,
+ &bytes_in_resource);
+
+ icon_dir->idEntries[index].bWidth = static_cast<BYTE>(bitmap.width());
+ icon_dir->idEntries[index].bHeight = static_cast<BYTE>(bitmap.height());
+ icon_dir->idEntries[index].wPlanes = 1;
+ icon_dir->idEntries[index].wBitCount = 32;
+ icon_dir->idEntries[index].dwBytesInRes = bytes_in_resource;
+ icon_dir->idEntries[index].dwImageOffset = image_offset;
+ icon_image->icHeader.biSize = sizeof(BITMAPINFOHEADER);
+
+ // The width field in the BITMAPINFOHEADER structure accounts for the height
+ // of both the AND mask and the XOR mask so we need to multiply the bitmap's
+ // height by 2. The same does NOT apply to the width field.
+ icon_image->icHeader.biHeight = bitmap.height() * 2;
+ icon_image->icHeader.biWidth = bitmap.width();
+ icon_image->icHeader.biPlanes = 1;
+ icon_image->icHeader.biBitCount = 32;
+
+ // We use a helper function for copying to actual bits from the SkBitmap
+ // object into the appropriate space in the buffer. We use a helper function
+ // (rather than just copying the bits) because there is no way to specify the
+ // orientation (bottom-up vs. top-down) of a bitmap residing in a .ico file.
+ // Thus, if we just copy the bits, we'll end up with a bottom up bitmap in
+ // the .ico file which will result in the icon being displayed upside down.
+ // The helper function copies the image into the buffer one scanline at a
+ // time.
+ //
+ // Note that we don't need to initialize the AND mask since the memory
+ // allocated for the icon data buffer was initialized to zero. The icon we
+ // create will therefore use an AND mask containing only zeros, which is OK
+ // because the underlying image has an alpha channel. An AND mask containing
+ // only zeros essentially means we'll initially treat all the pixels as
+ // opaque.
+ unsigned char* image_addr = reinterpret_cast<unsigned char*>(icon_image);
+ unsigned char* xor_mask_addr = image_addr + sizeof(BITMAPINFOHEADER);
+ CopySkBitmapBitsIntoIconBuffer(bitmap, xor_mask_addr, xor_mask_size);
+ *image_byte_count = bytes_in_resource;
+}
+
+void IconUtil::CopySkBitmapBitsIntoIconBuffer(const SkBitmap& bitmap,
+ unsigned char* buffer,
+ size_t buffer_size) {
+ SkAutoLockPixels bitmap_lock(bitmap);
+ unsigned char* bitmap_ptr = static_cast<unsigned char*>(bitmap.getPixels());
+ size_t bitmap_size = bitmap.height() * bitmap.width() * 4;
+ DCHECK_EQ(buffer_size, bitmap_size);
+ for (size_t i = 0; i < bitmap_size; i += bitmap.width() * 4) {
+ memcpy(buffer + bitmap_size - bitmap.width() * 4 - i,
+ bitmap_ptr + i,
+ bitmap.width() * 4);
+ }
+}
+
+void IconUtil::CreateResizedBitmapSet(const SkBitmap& bitmap_to_resize,
+ std::vector<SkBitmap>* bitmaps) {
+ DCHECK(bitmaps != NULL);
+ DCHECK(bitmaps->empty());
+
+ bool inserted_original_bitmap = false;
+ for (size_t i = 0; i < arraysize(icon_dimensions_); i++) {
+ // If the dimensions of the bitmap we are resizing are the same as the
+ // current dimensions, then we should insert the bitmap and not a resized
+ // bitmap. If the bitmap's dimensions are smaller, we insert our bitmap
+ // first so that the bitmaps we return in the vector are sorted based on
+ // their dimensions.
+ if (!inserted_original_bitmap) {
+ if ((bitmap_to_resize.width() == icon_dimensions_[i]) &&
+ (bitmap_to_resize.height() == icon_dimensions_[i])) {
+ bitmaps->push_back(bitmap_to_resize);
+ inserted_original_bitmap = true;
+ continue;
+ }
+
+ if ((bitmap_to_resize.width() < icon_dimensions_[i]) &&
+ (bitmap_to_resize.height() < icon_dimensions_[i])) {
+ bitmaps->push_back(bitmap_to_resize);
+ inserted_original_bitmap = true;
+ }
+ }
+ bitmaps->push_back(skia::ImageOperations::Resize(
+ bitmap_to_resize, skia::ImageOperations::RESIZE_LANCZOS3,
+ icon_dimensions_[i], icon_dimensions_[i]));
+ }
+
+ if (!inserted_original_bitmap)
+ bitmaps->push_back(bitmap_to_resize);
+}
+
+size_t IconUtil::ComputeIconFileBufferSize(const std::vector<SkBitmap>& set) {
+ DCHECK(!set.empty());
+
+ // We start by counting the bytes for the structures that don't depend on the
+ // number of icon images. Note that sizeof(ICONDIR) already accounts for a
+ // single ICONDIRENTRY structure, which is why we subtract one from the
+ // number of bitmaps.
+ size_t total_buffer_size = sizeof(ICONDIR);
+ size_t bitmap_count = set.size();
+ total_buffer_size += sizeof(ICONDIRENTRY) * (bitmap_count - 1);
+ DCHECK_GE(bitmap_count, arraysize(icon_dimensions_));
+
+ // Add the bitmap specific structure sizes.
+ for (size_t i = 0; i < bitmap_count; i++) {
+ size_t xor_mask_size, bytes_in_resource;
+ ComputeBitmapSizeComponents(set[i],
+ &xor_mask_size,
+ &bytes_in_resource);
+ total_buffer_size += bytes_in_resource;
+ }
+ return total_buffer_size;
+}
+
+void IconUtil::ComputeBitmapSizeComponents(const SkBitmap& bitmap,
+ size_t* xor_mask_size,
+ size_t* bytes_in_resource) {
+ // The XOR mask size is easy to calculate since we only deal with 32bpp
+ // images.
+ *xor_mask_size = bitmap.width() * bitmap.height() * 4;
+
+ // Computing the AND mask is a little trickier since it is a monochrome
+ // bitmap (regardless of the number of bits per pixels used in the XOR mask).
+ // There are two things we must make sure we do when computing the AND mask
+ // size:
+ //
+ // 1. Make sure the right number of bytes is allocated for each AND mask
+ // scan line in case the number of pixels in the image is not divisible by
+ // 8. For example, in a 15X15 image, 15 / 8 is one byte short of
+ // containing the number of bits we need in order to describe a single
+ // image scan line so we need to add a byte. Thus, we need 2 bytes instead
+ // of 1 for each scan line.
+ //
+ // 2. Make sure each scan line in the AND mask is 4 byte aligned (so that the
+ // total icon image has a 4 byte alignment). In the 15X15 image example
+ // above, we can not use 2 bytes so we increase it to the next multiple of
+ // 4 which is 4.
+ //
+ // Once we compute the size for a singe AND mask scan line, we multiply that
+ // number by the image height in order to get the total number of bytes for
+ // the AND mask. Thus, for a 15X15 image, we need 15 * 4 which is 60 bytes
+ // for the monochrome bitmap representing the AND mask.
+ size_t and_line_length = (bitmap.width() + 7) >> 3;
+ and_line_length = (and_line_length + 3) & ~3;
+ size_t and_mask_size = and_line_length * bitmap.height();
+ size_t masks_size = *xor_mask_size + and_mask_size;
+ *bytes_in_resource = masks_size + sizeof(BITMAPINFOHEADER);
+}
diff --git a/ui/gfx/icon_util.h b/ui/gfx/icon_util.h
new file mode 100644
index 0000000..ae23601
--- /dev/null
+++ b/ui/gfx/icon_util.h
@@ -0,0 +1,194 @@
+// 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.
+
+#ifndef UI_GFX_ICON_UTIL_H_
+#define UI_GFX_ICON_UTIL_H_
+#pragma once
+
+#include <windows.h>
+#include <string>
+#include <vector>
+#include "base/basictypes.h"
+
+namespace gfx {
+class Size;
+}
+class FilePath;
+class SkBitmap;
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// The IconUtil class contains helper functions for manipulating Windows icons.
+// The class interface contains methods for converting an HICON handle into an
+// SkBitmap object and vice versa. The class can also create a .ico file given
+// a PNG image contained in an SkBitmap object. The following code snippet
+// shows an example usage of IconUtil::CreateHICONFromSkBitmap():
+//
+// SkBitmap bitmap;
+//
+// // Fill |bitmap| with valid data
+// bitmap.setConfig(...);
+// bitmap.allocPixels();
+//
+// ...
+//
+// // Convert the bitmap into a Windows HICON
+// HICON icon = IconUtil::CreateHICONFromSkBitmap(bitmap);
+// if (icon == NULL) {
+// // Handle error
+// ...
+// }
+//
+// // Use the icon with a WM_SETICON message
+// ::SendMessage(hwnd, WM_SETICON, static_cast<WPARAM>(ICON_BIG),
+// reinterpret_cast<LPARAM>(icon));
+//
+// // Destroy the icon when we are done
+// ::DestroyIcon(icon);
+//
+///////////////////////////////////////////////////////////////////////////////
+class IconUtil {
+ public:
+ // Given an SkBitmap object, the function converts the bitmap to a Windows
+ // icon and returns the corresponding HICON handle. If the function cannot
+ // convert the bitmap, NULL is returned.
+ //
+ // The client is responsible for destroying the icon when it is no longer
+ // needed by calling ::DestroyIcon().
+ static HICON CreateHICONFromSkBitmap(const SkBitmap& bitmap);
+
+ // Given a valid HICON handle representing an icon, this function converts
+ // the icon into an SkBitmap object containing an ARGB bitmap using the
+ // dimensions specified in |s|. |s| must specify valid dimensions (both
+ // width() an height() must be greater than zero). If the function cannot
+ // convert the icon to a bitmap (most probably due to an invalid parameter),
+ // the return value is NULL.
+ //
+ // The client owns the returned bitmap object and is responsible for deleting
+ // it when it is no longer needed.
+ static SkBitmap* CreateSkBitmapFromHICON(HICON icon, const gfx::Size& s);
+
+ // Given an initialized SkBitmap object and a file name, this function
+ // creates a .ico file with the given name using the provided bitmap. The
+ // icon file is created with multiple icon images of varying predefined
+ // dimensions because Windows uses different image sizes when loading icons,
+ // depending on where the icon is drawn (ALT+TAB window, desktop shortcut,
+ // Quick Launch, etc.). |icon_file_name| needs to specify the full path for
+ // the desired .ico file.
+ //
+ // The function returns true on success and false otherwise.
+ static bool CreateIconFileFromSkBitmap(const SkBitmap& bitmap,
+ const FilePath& icon_path);
+
+ private:
+ // The icon format is published in the MSDN but there is no definition of
+ // the icon file structures in any of the Windows header files so we need to
+ // define these structure within the class. We must make sure we use 2 byte
+ // packing so that the structures are layed out properly within the file.
+#pragma pack(push)
+#pragma pack(2)
+
+ // ICONDIRENTRY contains meta data for an individual icon image within a
+ // .ico file.
+ struct ICONDIRENTRY {
+ BYTE bWidth;
+ BYTE bHeight;
+ BYTE bColorCount;
+ BYTE bReserved;
+ WORD wPlanes;
+ WORD wBitCount;
+ DWORD dwBytesInRes;
+ DWORD dwImageOffset;
+ };
+
+ // ICONDIR Contains information about all the icon images contained within a
+ // single .ico file.
+ struct ICONDIR {
+ WORD idReserved;
+ WORD idType;
+ WORD idCount;
+ ICONDIRENTRY idEntries[1];
+ };
+
+ // Contains the actual icon image.
+ struct ICONIMAGE {
+ BITMAPINFOHEADER icHeader;
+ RGBQUAD icColors[1];
+ BYTE icXOR[1];
+ BYTE icAND[1];
+ };
+#pragma pack(pop)
+
+ // Used for indicating that the .ico contains an icon (rather than a cursor)
+ // image. This value is set in the |idType| field of the ICONDIR structure.
+ static const int kResourceTypeIcon = 1;
+
+ // The dimensions of the icon images we insert into the .ico file.
+ static const int icon_dimensions_[];
+
+ // Returns true if any pixel in the given pixels buffer has an non-zero alpha.
+ static bool PixelsHaveAlpha(const uint32* pixels, size_t num_pixels);
+
+ // A helper function that initializes a BITMAPV5HEADER structure with a set
+ // of values.
+ static void InitializeBitmapHeader(BITMAPV5HEADER* header, int width,
+ int height);
+
+ // Given a single SkBitmap object and pointers to the corresponding icon
+ // structures within the icon data buffer, this function sets the image
+ // information (dimensions, color depth, etc.) in the icon structures and
+ // also copies the underlying icon image into the appropriate location.
+ //
+ // The function will set the data pointed to by |image_byte_count| with the
+ // number of image bytes written to the buffer. Note that the number of bytes
+ // includes only the image data written into the memory pointed to by
+ // |icon_image|.
+ static void SetSingleIconImageInformation(const SkBitmap& bitmap,
+ size_t index,
+ ICONDIR* icon_dir,
+ ICONIMAGE* icon_image,
+ size_t image_offset,
+ size_t* image_byte_count);
+
+ // Copies the bits of an SkBitmap object into a buffer holding the bits of
+ // the corresponding image for an icon within the .ico file.
+ static void CopySkBitmapBitsIntoIconBuffer(const SkBitmap& bitmap,
+ unsigned char* buffer,
+ size_t buffer_size);
+
+ // Given a single bitmap, this function creates a set of bitmaps with
+ // specific dimensions by resizing the given bitmap to the appropriate sizes.
+ static void CreateResizedBitmapSet(const SkBitmap& bitmap_to_resize,
+ std::vector<SkBitmap>* bitmaps);
+
+ // Given a set of bitmaps with varying dimensions, this function computes
+ // the amount of memory needed in order to store the bitmaps as image icons
+ // in a .ico file.
+ static size_t ComputeIconFileBufferSize(const std::vector<SkBitmap>& set);
+
+ // A helper function for computing various size components of a given bitmap.
+ // The different sizes can be used within the various .ico file structures.
+ //
+ // |xor_mask_size| - the size, in bytes, of the XOR mask in the ICONIMAGE
+ // structure.
+ // |and_mask_size| - the size, in bytes, of the AND mask in the ICONIMAGE
+ // structure.
+ // |bytes_in_resource| - the total number of bytes set in the ICONIMAGE
+ // structure. This value is equal to the sum of the
+ // bytes in the AND mask and the XOR mask plus the size
+ // of the BITMAPINFOHEADER structure. Note that since
+ // only 32bpp are handled by the IconUtil class, the
+ // icColors field in the ICONIMAGE structure is ignored
+ // and is not accounted for when computing the
+ // different size components.
+ static void ComputeBitmapSizeComponents(const SkBitmap& bitmap,
+ size_t* xor_mask_size,
+ size_t* bytes_in_resource);
+
+ // Prevent clients from instantiating objects of that class by declaring the
+ // ctor/dtor as private.
+ DISALLOW_IMPLICIT_CONSTRUCTORS(IconUtil);
+};
+
+#endif // UI_GFX_ICON_UTIL_H_
diff --git a/ui/gfx/icon_util_unittest.cc b/ui/gfx/icon_util_unittest.cc
new file mode 100644
index 0000000..43eca49
--- /dev/null
+++ b/ui/gfx/icon_util_unittest.cc
@@ -0,0 +1,256 @@
+// 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 "base/file_util.h"
+#include "base/path_service.h"
+#include "base/scoped_ptr.h"
+#include "gfx/gfx_paths.h"
+#include "gfx/icon_util.h"
+#include "gfx/size.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+
+namespace {
+
+static const char kSmallIconName[] = "icon_util/16_X_16_icon.ico";
+static const char kLargeIconName[] = "icon_util/128_X_128_icon.ico";
+static const char kTempIconFilename[] = "temp_test_icon.ico";
+
+class IconUtilTest : public testing::Test {
+ public:
+ IconUtilTest() {
+ PathService::Get(gfx::DIR_TEST_DATA, &test_data_directory_);
+ }
+ ~IconUtilTest() {}
+
+ static const int kSmallIconWidth = 16;
+ static const int kSmallIconHeight = 16;
+ static const int kLargeIconWidth = 128;
+ static const int kLargeIconHeight = 128;
+
+ // Given a file name for an .ico file and an image dimentions, this
+ // function loads the icon and returns an HICON handle.
+ HICON LoadIconFromFile(const FilePath& filename, int width, int height) {
+ HICON icon = static_cast<HICON>(LoadImage(NULL,
+ filename.value().c_str(),
+ IMAGE_ICON,
+ width,
+ height,
+ LR_LOADTRANSPARENT | LR_LOADFROMFILE));
+ return icon;
+ }
+
+ protected:
+ // The root directory for test files.
+ FilePath test_data_directory_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(IconUtilTest);
+};
+
+} // namespace
+
+// The following test case makes sure IconUtil::SkBitmapFromHICON fails
+// gracefully when called with invalid input parameters.
+TEST_F(IconUtilTest, TestIconToBitmapInvalidParameters) {
+ FilePath icon_filename = test_data_directory_.AppendASCII(kSmallIconName);
+ gfx::Size icon_size(kSmallIconWidth, kSmallIconHeight);
+ HICON icon = LoadIconFromFile(icon_filename,
+ icon_size.width(),
+ icon_size.height());
+ ASSERT_TRUE(icon != NULL);
+
+ // Invalid size parameter.
+ gfx::Size invalid_icon_size(kSmallIconHeight, 0);
+ EXPECT_EQ(IconUtil::CreateSkBitmapFromHICON(icon, invalid_icon_size),
+ static_cast<SkBitmap*>(NULL));
+
+ // Invalid icon.
+ EXPECT_EQ(IconUtil::CreateSkBitmapFromHICON(NULL, icon_size),
+ static_cast<SkBitmap*>(NULL));
+
+ // The following code should succeed.
+ scoped_ptr<SkBitmap> bitmap;
+ bitmap.reset(IconUtil::CreateSkBitmapFromHICON(icon, icon_size));
+ EXPECT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL));
+ ::DestroyIcon(icon);
+}
+
+// The following test case makes sure IconUtil::CreateHICONFromSkBitmap fails
+// gracefully when called with invalid input parameters.
+TEST_F(IconUtilTest, TestBitmapToIconInvalidParameters) {
+ HICON icon = NULL;
+ scoped_ptr<SkBitmap> bitmap;
+
+ // Wrong bitmap format.
+ bitmap.reset(new SkBitmap);
+ ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL));
+ bitmap->setConfig(SkBitmap::kA8_Config, kSmallIconWidth, kSmallIconHeight);
+ icon = IconUtil::CreateHICONFromSkBitmap(*bitmap);
+ EXPECT_EQ(icon, static_cast<HICON>(NULL));
+
+ // Invalid bitmap size.
+ bitmap.reset(new SkBitmap);
+ ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL));
+ bitmap->setConfig(SkBitmap::kARGB_8888_Config, 0, 0);
+ icon = IconUtil::CreateHICONFromSkBitmap(*bitmap);
+ EXPECT_EQ(icon, static_cast<HICON>(NULL));
+
+ // Valid bitmap configuration but no pixels allocated.
+ bitmap.reset(new SkBitmap);
+ ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL));
+ bitmap->setConfig(SkBitmap::kARGB_8888_Config,
+ kSmallIconWidth,
+ kSmallIconHeight);
+ icon = IconUtil::CreateHICONFromSkBitmap(*bitmap);
+ EXPECT_TRUE(icon == NULL);
+}
+
+// The following test case makes sure IconUtil::CreateIconFileFromSkBitmap
+// fails gracefully when called with invalid input parameters.
+TEST_F(IconUtilTest, TestCreateIconFileInvalidParameters) {
+ scoped_ptr<SkBitmap> bitmap;
+ FilePath valid_icon_filename = test_data_directory_.AppendASCII(
+ kSmallIconName);
+ FilePath invalid_icon_filename(FILE_PATH_LITERAL("C:\\<>?.ico"));
+
+ // Wrong bitmap format.
+ bitmap.reset(new SkBitmap);
+ ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL));
+ bitmap->setConfig(SkBitmap::kA8_Config, kSmallIconWidth, kSmallIconHeight);
+ EXPECT_FALSE(IconUtil::CreateIconFileFromSkBitmap(*bitmap,
+ valid_icon_filename));
+
+ // Invalid bitmap size.
+ bitmap.reset(new SkBitmap);
+ ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL));
+ bitmap->setConfig(SkBitmap::kARGB_8888_Config, 0, 0);
+ EXPECT_FALSE(IconUtil::CreateIconFileFromSkBitmap(*bitmap,
+ valid_icon_filename));
+
+ // Bitmap with no allocated pixels.
+ bitmap.reset(new SkBitmap);
+ ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL));
+ bitmap->setConfig(SkBitmap::kARGB_8888_Config,
+ kSmallIconWidth,
+ kSmallIconHeight);
+ EXPECT_FALSE(IconUtil::CreateIconFileFromSkBitmap(*bitmap,
+ valid_icon_filename));
+
+ // Invalid file name.
+ bitmap->allocPixels();
+ // Setting the pixels to black.
+ memset(bitmap->getPixels(), 0, bitmap->width() * bitmap->height() * 4);
+ EXPECT_FALSE(IconUtil::CreateIconFileFromSkBitmap(*bitmap,
+ invalid_icon_filename));
+}
+
+// This test case makes sure that when we load an icon from disk and convert
+// the HICON into a bitmap, the bitmap has the expected format and dimentions.
+TEST_F(IconUtilTest, TestCreateSkBitmapFromHICON) {
+ scoped_ptr<SkBitmap> bitmap;
+ FilePath small_icon_filename = test_data_directory_.AppendASCII(
+ kSmallIconName);
+ gfx::Size small_icon_size(kSmallIconWidth, kSmallIconHeight);
+ HICON small_icon = LoadIconFromFile(small_icon_filename,
+ small_icon_size.width(),
+ small_icon_size.height());
+ ASSERT_NE(small_icon, static_cast<HICON>(NULL));
+ bitmap.reset(IconUtil::CreateSkBitmapFromHICON(small_icon, small_icon_size));
+ ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL));
+ EXPECT_EQ(bitmap->width(), small_icon_size.width());
+ EXPECT_EQ(bitmap->height(), small_icon_size.height());
+ EXPECT_EQ(bitmap->config(), SkBitmap::kARGB_8888_Config);
+ ::DestroyIcon(small_icon);
+
+ FilePath large_icon_filename = test_data_directory_.AppendASCII(
+ kLargeIconName);
+ gfx::Size large_icon_size(kLargeIconWidth, kLargeIconHeight);
+ HICON large_icon = LoadIconFromFile(large_icon_filename,
+ large_icon_size.width(),
+ large_icon_size.height());
+ ASSERT_NE(large_icon, static_cast<HICON>(NULL));
+ bitmap.reset(IconUtil::CreateSkBitmapFromHICON(large_icon, large_icon_size));
+ ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL));
+ EXPECT_EQ(bitmap->width(), large_icon_size.width());
+ EXPECT_EQ(bitmap->height(), large_icon_size.height());
+ EXPECT_EQ(bitmap->config(), SkBitmap::kARGB_8888_Config);
+ ::DestroyIcon(large_icon);
+}
+
+// This test case makes sure that when an HICON is created from an SkBitmap,
+// the returned handle is valid and refers to an icon with the expected
+// dimentions color depth etc.
+TEST_F(IconUtilTest, TestBasicCreateHICONFromSkBitmap) {
+ scoped_ptr<SkBitmap> bitmap;
+ bitmap.reset(new SkBitmap);
+ ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL));
+ bitmap->setConfig(SkBitmap::kARGB_8888_Config,
+ kSmallIconWidth,
+ kSmallIconHeight);
+ bitmap->allocPixels();
+ HICON icon = IconUtil::CreateHICONFromSkBitmap(*bitmap);
+ EXPECT_NE(icon, static_cast<HICON>(NULL));
+ ICONINFO icon_info;
+ ASSERT_TRUE(::GetIconInfo(icon, &icon_info));
+ EXPECT_TRUE(icon_info.fIcon);
+
+ // Now that have the icon information, we should obtain the specification of
+ // the icon's bitmap and make sure it matches the specification of the
+ // SkBitmap we started with.
+ //
+ // The bitmap handle contained in the icon information is a handle to a
+ // compatible bitmap so we need to call ::GetDIBits() in order to retrieve
+ // the bitmap's header information.
+ BITMAPINFO bitmap_info;
+ ::ZeroMemory(&bitmap_info, sizeof(BITMAPINFO));
+ bitmap_info.bmiHeader.biSize = sizeof(BITMAPINFO);
+ HDC hdc = ::GetDC(NULL);
+ int result = ::GetDIBits(hdc,
+ icon_info.hbmColor,
+ 0,
+ kSmallIconWidth,
+ NULL,
+ &bitmap_info,
+ DIB_RGB_COLORS);
+ ASSERT_GT(result, 0);
+ EXPECT_EQ(bitmap_info.bmiHeader.biWidth, kSmallIconWidth);
+ EXPECT_EQ(bitmap_info.bmiHeader.biHeight, kSmallIconHeight);
+ EXPECT_EQ(bitmap_info.bmiHeader.biPlanes, 1);
+ EXPECT_EQ(bitmap_info.bmiHeader.biBitCount, 32);
+ ::ReleaseDC(NULL, hdc);
+ ::DestroyIcon(icon);
+}
+
+// The following test case makes sure IconUtil::CreateIconFileFromSkBitmap
+// creates a valid .ico file given an SkBitmap.
+TEST_F(IconUtilTest, TestCreateIconFile) {
+ scoped_ptr<SkBitmap> bitmap;
+ FilePath icon_filename = test_data_directory_.AppendASCII(kTempIconFilename);
+
+ // Allocating the bitmap.
+ bitmap.reset(new SkBitmap);
+ ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL));
+ bitmap->setConfig(SkBitmap::kARGB_8888_Config,
+ kSmallIconWidth,
+ kSmallIconHeight);
+ bitmap->allocPixels();
+
+ // Setting the pixels to black.
+ memset(bitmap->getPixels(), 0, bitmap->width() * bitmap->height() * 4);
+
+ EXPECT_TRUE(IconUtil::CreateIconFileFromSkBitmap(*bitmap,
+ icon_filename));
+
+ // We are currently only testing that it is possible to load an icon from
+ // the .ico file we just created. We don't really check the additional icon
+ // images created by IconUtil::CreateIconFileFromSkBitmap.
+ HICON icon = LoadIconFromFile(icon_filename,
+ kSmallIconWidth,
+ kSmallIconHeight);
+ EXPECT_NE(icon, static_cast<HICON>(NULL));
+ if (icon != NULL) {
+ ::DestroyIcon(icon);
+ }
+}
diff --git a/ui/gfx/insets.cc b/ui/gfx/insets.cc
new file mode 100644
index 0000000..06cc6aa
--- /dev/null
+++ b/ui/gfx/insets.cc
@@ -0,0 +1,16 @@
+// 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/insets.h"
+
+#include "base/string_util.h"
+
+namespace gfx {
+
+std::string Insets::ToString() const {
+ // Print members in the same order of the constructor parameters.
+ return StringPrintf("%d,%d,%d,%d", top_, left_, bottom_, right_);
+}
+
+} // namespace gfx
diff --git a/ui/gfx/insets.h b/ui/gfx/insets.h
new file mode 100644
index 0000000..31909b4
--- /dev/null
+++ b/ui/gfx/insets.h
@@ -0,0 +1,94 @@
+// 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 UI_GFX_INSETS_H_
+#define UI_GFX_INSETS_H_
+#pragma once
+
+#include "build/build_config.h"
+
+#if defined(OS_POSIX) && !defined(OS_MACOSX)
+#include <gtk/gtkstyle.h>
+#endif
+
+#include <string>
+
+namespace gfx {
+
+//
+// An insets represents the borders of a container (the space the container must
+// leave at each of its edges).
+//
+
+class Insets {
+ public:
+ Insets() : top_(0), left_(0), bottom_(0), right_(0) {}
+ Insets(int top, int left, int bottom, int right)
+ : top_(top),
+ left_(left),
+ bottom_(bottom),
+ right_(right) {}
+#if defined(OS_POSIX) && !defined(OS_MACOSX)
+ explicit Insets(const GtkBorder& border)
+ : top_(border.top),
+ left_(border.left),
+ bottom_(border.bottom),
+ right_(border.right) {}
+#endif
+
+ ~Insets() {}
+
+ int top() const { return top_; }
+ int left() const { return left_; }
+ int bottom() const { return bottom_; }
+ int right() const { return right_; }
+
+ // Returns the total width taken up by the insets, which is the sum of the
+ // left and right insets.
+ int width() const { return left_ + right_; }
+
+ // Returns the total height taken up by the insets, which is the sum of the
+ // top and bottom insets.
+ int height() const { return top_ + bottom_; }
+
+ // Returns true if the insets are empty.
+ bool empty() const { return width() == 0 && height() == 0; }
+
+ void Set(int top, int left, int bottom, int right) {
+ top_ = top;
+ left_ = left;
+ bottom_ = bottom;
+ right_ = right;
+ }
+
+ bool operator==(const Insets& insets) const {
+ return top_ == insets.top_ && left_ == insets.left_ &&
+ bottom_ == insets.bottom_ && right_ == insets.right_;
+ }
+
+ bool operator!=(const Insets& insets) const {
+ return !(*this == insets);
+ }
+
+ Insets& operator+=(const Insets& insets) {
+ top_ += insets.top_;
+ left_ += insets.left_;
+ bottom_ += insets.bottom_;
+ right_ += insets.right_;
+ return *this;
+ }
+
+ // Returns a string representation of the insets.
+ std::string ToString() const;
+
+ private:
+ int top_;
+ int left_;
+ int bottom_;
+ int right_;
+};
+
+} // namespace gfx
+
+#endif // UI_GFX_INSETS_H_
diff --git a/ui/gfx/insets_unittest.cc b/ui/gfx/insets_unittest.cc
new file mode 100644
index 0000000..30d6d7f
--- /dev/null
+++ b/ui/gfx/insets_unittest.cc
@@ -0,0 +1,66 @@
+// 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/insets.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(InsetsTest, InsetsDefault) {
+ gfx::Insets insets;
+ EXPECT_EQ(0, insets.top());
+ EXPECT_EQ(0, insets.left());
+ EXPECT_EQ(0, insets.bottom());
+ EXPECT_EQ(0, insets.right());
+ EXPECT_EQ(0, insets.width());
+ EXPECT_EQ(0, insets.height());
+ EXPECT_TRUE(insets.empty());
+}
+
+TEST(InsetsTest, Insets) {
+ gfx::Insets insets(1, 2, 3, 4);
+ EXPECT_EQ(1, insets.top());
+ EXPECT_EQ(2, insets.left());
+ EXPECT_EQ(3, insets.bottom());
+ EXPECT_EQ(4, insets.right());
+ EXPECT_EQ(6, insets.width()); // Left + right.
+ EXPECT_EQ(4, insets.height()); // Top + bottom.
+ EXPECT_FALSE(insets.empty());
+}
+
+TEST(InsetsTest, Set) {
+ gfx::Insets insets;
+ insets.Set(1, 2, 3, 4);
+ EXPECT_EQ(1, insets.top());
+ EXPECT_EQ(2, insets.left());
+ EXPECT_EQ(3, insets.bottom());
+ EXPECT_EQ(4, insets.right());
+}
+
+TEST(InsetsTest, Add) {
+ gfx::Insets insets;
+ insets.Set(1, 2, 3, 4);
+ insets += gfx::Insets(5, 6, 7, 8);
+ EXPECT_EQ(6, insets.top());
+ EXPECT_EQ(8, insets.left());
+ EXPECT_EQ(10, insets.bottom());
+ EXPECT_EQ(12, insets.right());
+}
+
+TEST(InsetsTest, Equality) {
+ gfx::Insets insets1;
+ insets1.Set(1, 2, 3, 4);
+ gfx::Insets insets2;
+ // Test operator== and operator!=.
+ EXPECT_FALSE(insets1 == insets2);
+ EXPECT_TRUE(insets1 != insets2);
+
+ insets2.Set(1, 2, 3, 4);
+ EXPECT_TRUE(insets1 == insets2);
+ EXPECT_FALSE(insets1 != insets2);
+}
+
+TEST(InsetsTest, ToString) {
+ gfx::Insets insets(1, 2, 3, 4);
+ EXPECT_EQ("1,2,3,4", insets.ToString());
+}
diff --git a/ui/gfx/native_theme_linux.cc b/ui/gfx/native_theme_linux.cc
new file mode 100644
index 0000000..b2087fe
--- /dev/null
+++ b/ui/gfx/native_theme_linux.cc
@@ -0,0 +1,941 @@
+// 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/native_theme_linux.h"
+
+#include <limits>
+
+#include "base/logging.h"
+#include "gfx/codec/png_codec.h"
+#include "gfx/color_utils.h"
+#include "gfx/gfx_module.h"
+#include "gfx/size.h"
+#include "gfx/rect.h"
+#include "grit/gfx_resources.h"
+#include "third_party/skia/include/effects/SkGradientShader.h"
+
+namespace gfx {
+
+unsigned int NativeThemeLinux::button_length_ = 14;
+unsigned int NativeThemeLinux::scrollbar_width_ = 15;
+unsigned int NativeThemeLinux::thumb_inactive_color_ = 0xeaeaea;
+unsigned int NativeThemeLinux::thumb_active_color_ = 0xf4f4f4;
+unsigned int NativeThemeLinux::track_color_ = 0xd3d3d3;
+
+// These are the default dimensions of radio buttons and checkboxes.
+static const int kCheckboxAndRadioWidth = 13;
+static const int kCheckboxAndRadioHeight = 13;
+
+// These sizes match the sizes in Chromium Win.
+static const int kSliderThumbWidth = 11;
+static const int kSliderThumbHeight = 21;
+
+static const SkColor kSliderTrackBackgroundColor =
+ SkColorSetRGB(0xe3, 0xdd, 0xd8);
+static const SkColor kSliderThumbLightGrey = SkColorSetRGB(0xf4, 0xf2, 0xef);
+static const SkColor kSliderThumbDarkGrey = SkColorSetRGB(0xea, 0xe5, 0xe0);
+static const SkColor kSliderThumbBorderDarkGrey =
+ SkColorSetRGB(0x9d, 0x96, 0x8e);
+
+#if !defined(OS_CHROMEOS)
+// Chromeos has a different look.
+// static
+NativeThemeLinux* NativeThemeLinux::instance() {
+ // The global NativeThemeLinux instance.
+ static NativeThemeLinux s_native_theme;
+ return &s_native_theme;
+}
+#endif
+
+// Get lightness adjusted color.
+static SkColor BrightenColor(const color_utils::HSL& hsl, SkAlpha alpha,
+ double lightness_amount) {
+ color_utils::HSL adjusted = hsl;
+ adjusted.l += lightness_amount;
+ if (adjusted.l > 1.0)
+ adjusted.l = 1.0;
+ if (adjusted.l < 0.0)
+ adjusted.l = 0.0;
+
+ return color_utils::HSLToSkColor(adjusted, alpha);
+}
+
+static SkBitmap* GfxGetBitmapNamed(int key) {
+ base::StringPiece data = GfxModule::GetResource(key);
+ if (!data.size()) {
+ NOTREACHED() << "Unable to load image resource " << key;
+ return NULL;
+ }
+
+ SkBitmap bitmap;
+ if (!gfx::PNGCodec::Decode(
+ reinterpret_cast<const unsigned char*>(data.data()),
+ data.size(), &bitmap)) {
+ NOTREACHED() << "Unable to decode image resource " << key;
+ return NULL;
+ }
+
+ return new SkBitmap(bitmap);
+}
+
+NativeThemeLinux::NativeThemeLinux() {
+}
+
+NativeThemeLinux::~NativeThemeLinux() {
+}
+
+gfx::Size NativeThemeLinux::GetPartSize(Part part) const {
+ switch (part) {
+ case kScrollbarDownArrow:
+ case kScrollbarUpArrow:
+ return gfx::Size(scrollbar_width_, button_length_);
+ case kScrollbarLeftArrow:
+ case kScrollbarRightArrow:
+ return gfx::Size(button_length_, scrollbar_width_);
+ case kScrollbarHorizontalThumb:
+ // This matches Firefox on Linux.
+ return gfx::Size(2 * scrollbar_width_, scrollbar_width_);
+ case kScrollbarVerticalThumb:
+ // This matches Firefox on Linux.
+ return gfx::Size(scrollbar_width_, 2 * scrollbar_width_);
+ break;
+ case kScrollbarHorizontalTrack:
+ return gfx::Size(0, scrollbar_width_);
+ case kScrollbarVerticalTrack:
+ return gfx::Size(scrollbar_width_, 0);
+ case kCheckbox:
+ case kRadio:
+ return gfx::Size(kCheckboxAndRadioWidth, kCheckboxAndRadioHeight);
+ case kSliderThumb:
+ // These sizes match the sizes in Chromium Win.
+ return gfx::Size(kSliderThumbWidth, kSliderThumbHeight);
+ case kInnerSpinButton:
+ return gfx::Size(scrollbar_width_, 0);
+ case kPushButton:
+ case kTextField:
+ case kMenuList:
+ case kSliderTrack:
+ case kProgressBar:
+ return gfx::Size(); // No default size.
+ }
+ return gfx::Size();
+}
+
+void NativeThemeLinux::PaintArrowButton(
+ skia::PlatformCanvas* canvas,
+ const gfx::Rect& rect, Part direction, State state) {
+ int widthMiddle, lengthMiddle;
+ SkPaint paint;
+ if (direction == kScrollbarUpArrow || direction == kScrollbarDownArrow) {
+ widthMiddle = rect.width() / 2 + 1;
+ lengthMiddle = rect.height() / 2 + 1;
+ } else {
+ lengthMiddle = rect.width() / 2 + 1;
+ widthMiddle = rect.height() / 2 + 1;
+ }
+
+ // Calculate button color.
+ SkScalar trackHSV[3];
+ SkColorToHSV(track_color_, trackHSV);
+ SkColor buttonColor = SaturateAndBrighten(trackHSV, 0, 0.2);
+ SkColor backgroundColor = buttonColor;
+ if (state == kPressed) {
+ SkScalar buttonHSV[3];
+ SkColorToHSV(buttonColor, buttonHSV);
+ buttonColor = SaturateAndBrighten(buttonHSV, 0, -0.1);
+ } else if (state == kHovered) {
+ SkScalar buttonHSV[3];
+ SkColorToHSV(buttonColor, buttonHSV);
+ buttonColor = SaturateAndBrighten(buttonHSV, 0, 0.05);
+ }
+
+ SkIRect skrect;
+ skrect.set(rect.x(), rect.y(), rect.x() + rect.width(), rect.y()
+ + rect.height());
+ // Paint the background (the area visible behind the rounded corners).
+ paint.setColor(backgroundColor);
+ canvas->drawIRect(skrect, paint);
+
+ // Paint the button's outline and fill the middle
+ SkPath outline;
+ switch (direction) {
+ case kScrollbarUpArrow:
+ outline.moveTo(rect.x() + 0.5, rect.y() + rect.height() + 0.5);
+ outline.rLineTo(0, -(rect.height() - 2));
+ outline.rLineTo(2, -2);
+ outline.rLineTo(rect.width() - 5, 0);
+ outline.rLineTo(2, 2);
+ outline.rLineTo(0, rect.height() - 2);
+ break;
+ case kScrollbarDownArrow:
+ outline.moveTo(rect.x() + 0.5, rect.y() - 0.5);
+ outline.rLineTo(0, rect.height() - 2);
+ outline.rLineTo(2, 2);
+ outline.rLineTo(rect.width() - 5, 0);
+ outline.rLineTo(2, -2);
+ outline.rLineTo(0, -(rect.height() - 2));
+ break;
+ case kScrollbarRightArrow:
+ outline.moveTo(rect.x() - 0.5, rect.y() + 0.5);
+ outline.rLineTo(rect.width() - 2, 0);
+ outline.rLineTo(2, 2);
+ outline.rLineTo(0, rect.height() - 5);
+ outline.rLineTo(-2, 2);
+ outline.rLineTo(-(rect.width() - 2), 0);
+ break;
+ case kScrollbarLeftArrow:
+ outline.moveTo(rect.x() + rect.width() + 0.5, rect.y() + 0.5);
+ outline.rLineTo(-(rect.width() - 2), 0);
+ outline.rLineTo(-2, 2);
+ outline.rLineTo(0, rect.height() - 5);
+ outline.rLineTo(2, 2);
+ outline.rLineTo(rect.width() - 2, 0);
+ break;
+ default:
+ break;
+ }
+ outline.close();
+
+ paint.setStyle(SkPaint::kFill_Style);
+ paint.setColor(buttonColor);
+ canvas->drawPath(outline, paint);
+
+ paint.setAntiAlias(true);
+ paint.setStyle(SkPaint::kStroke_Style);
+ SkScalar thumbHSV[3];
+ SkColorToHSV(thumb_inactive_color_, thumbHSV);
+ paint.setColor(OutlineColor(trackHSV, thumbHSV));
+ canvas->drawPath(outline, paint);
+
+ // If the button is disabled or read-only, the arrow is drawn with the
+ // outline color.
+ if (state != kDisabled)
+ paint.setColor(SK_ColorBLACK);
+
+ paint.setAntiAlias(false);
+ paint.setStyle(SkPaint::kFill_Style);
+
+ SkPath path;
+ // The constants in this block of code are hand-tailored to produce good
+ // looking arrows without anti-aliasing.
+ switch (direction) {
+ case kScrollbarUpArrow:
+ path.moveTo(rect.x() + widthMiddle - 4, rect.y() + lengthMiddle + 2);
+ path.rLineTo(7, 0);
+ path.rLineTo(-4, -4);
+ break;
+ case kScrollbarDownArrow:
+ path.moveTo(rect.x() + widthMiddle - 4, rect.y() + lengthMiddle - 3);
+ path.rLineTo(7, 0);
+ path.rLineTo(-4, 4);
+ break;
+ case kScrollbarRightArrow:
+ path.moveTo(rect.x() + lengthMiddle - 3, rect.y() + widthMiddle - 4);
+ path.rLineTo(0, 7);
+ path.rLineTo(4, -4);
+ break;
+ case kScrollbarLeftArrow:
+ path.moveTo(rect.x() + lengthMiddle + 1, rect.y() + widthMiddle - 5);
+ path.rLineTo(0, 9);
+ path.rLineTo(-4, -4);
+ break;
+ default:
+ break;
+ }
+ path.close();
+
+ canvas->drawPath(path, paint);
+}
+
+void NativeThemeLinux::Paint(skia::PlatformCanvas* canvas,
+ Part part,
+ State state,
+ const gfx::Rect& rect,
+ const ExtraParams& extra) {
+ switch (part) {
+ case kScrollbarDownArrow:
+ case kScrollbarUpArrow:
+ case kScrollbarLeftArrow:
+ case kScrollbarRightArrow:
+ PaintArrowButton(canvas, rect, part, state);
+ break;
+ case kScrollbarHorizontalThumb:
+ case kScrollbarVerticalThumb:
+ PaintScrollbarThumb(canvas, part, state, rect);
+ break;
+ case kScrollbarHorizontalTrack:
+ case kScrollbarVerticalTrack:
+ PaintScrollbarTrack(canvas, part, state, extra.scrollbar_track, rect);
+ break;
+ case kCheckbox:
+ PaintCheckbox(canvas, state, rect, extra.button);
+ break;
+ case kRadio:
+ PaintRadio(canvas, state, rect, extra.button);
+ break;
+ case kPushButton:
+ PaintButton(canvas, state, rect, extra.button);
+ break;
+ case kTextField:
+ PaintTextField(canvas, state, rect, extra.text_field);
+ break;
+ case kMenuList:
+ PaintMenuList(canvas, state, rect, extra.menu_list);
+ break;
+ case kSliderTrack:
+ PaintSliderTrack(canvas, state, rect, extra.slider);
+ break;
+ case kSliderThumb:
+ PaintSliderThumb(canvas, state, rect, extra.slider);
+ break;
+ case kInnerSpinButton:
+ PaintInnerSpinButton(canvas, state, rect, extra.inner_spin);
+ break;
+ case kProgressBar:
+ PaintProgressBar(canvas, state, rect, extra.progress_bar);
+ break;
+ }
+}
+
+void NativeThemeLinux::PaintScrollbarTrack(skia::PlatformCanvas* canvas,
+ Part part,
+ State state,
+ const ScrollbarTrackExtraParams& extra_params,
+ const gfx::Rect& rect) {
+ SkPaint paint;
+ SkIRect skrect;
+
+ skrect.set(rect.x(), rect.y(), rect.right(), rect.bottom());
+ SkScalar track_hsv[3];
+ SkColorToHSV(track_color_, track_hsv);
+ paint.setColor(SaturateAndBrighten(track_hsv, 0, 0));
+ canvas->drawIRect(skrect, paint);
+
+ SkScalar thumb_hsv[3];
+ SkColorToHSV(thumb_inactive_color_, thumb_hsv);
+
+ paint.setColor(OutlineColor(track_hsv, thumb_hsv));
+ DrawBox(canvas, rect, paint);
+}
+
+void NativeThemeLinux::PaintScrollbarThumb(skia::PlatformCanvas* canvas,
+ Part part,
+ State state,
+ const gfx::Rect& rect) {
+ const bool hovered = state == kHovered;
+ const int midx = rect.x() + rect.width() / 2;
+ const int midy = rect.y() + rect.height() / 2;
+ const bool vertical = part == kScrollbarVerticalThumb;
+
+ SkScalar thumb[3];
+ SkColorToHSV(hovered ? thumb_active_color_ : thumb_inactive_color_, thumb);
+
+ SkPaint paint;
+ paint.setColor(SaturateAndBrighten(thumb, 0, 0.02));
+
+ SkIRect skrect;
+ if (vertical)
+ skrect.set(rect.x(), rect.y(), midx + 1, rect.y() + rect.height());
+ else
+ skrect.set(rect.x(), rect.y(), rect.x() + rect.width(), midy + 1);
+
+ canvas->drawIRect(skrect, paint);
+
+ paint.setColor(SaturateAndBrighten(thumb, 0, -0.02));
+
+ if (vertical) {
+ skrect.set(
+ midx + 1, rect.y(), rect.x() + rect.width(), rect.y() + rect.height());
+ } else {
+ skrect.set(
+ rect.x(), midy + 1, rect.x() + rect.width(), rect.y() + rect.height());
+ }
+
+ canvas->drawIRect(skrect, paint);
+
+ SkScalar track[3];
+ SkColorToHSV(track_color_, track);
+ paint.setColor(OutlineColor(track, thumb));
+ DrawBox(canvas, rect, paint);
+
+ if (rect.height() > 10 && rect.width() > 10) {
+ const int grippy_half_width = 2;
+ const int inter_grippy_offset = 3;
+ if (vertical) {
+ DrawHorizLine(canvas,
+ midx - grippy_half_width,
+ midx + grippy_half_width,
+ midy - inter_grippy_offset,
+ paint);
+ DrawHorizLine(canvas,
+ midx - grippy_half_width,
+ midx + grippy_half_width,
+ midy,
+ paint);
+ DrawHorizLine(canvas,
+ midx - grippy_half_width,
+ midx + grippy_half_width,
+ midy + inter_grippy_offset,
+ paint);
+ } else {
+ DrawVertLine(canvas,
+ midx - inter_grippy_offset,
+ midy - grippy_half_width,
+ midy + grippy_half_width,
+ paint);
+ DrawVertLine(canvas,
+ midx,
+ midy - grippy_half_width,
+ midy + grippy_half_width,
+ paint);
+ DrawVertLine(canvas,
+ midx + inter_grippy_offset,
+ midy - grippy_half_width,
+ midy + grippy_half_width,
+ paint);
+ }
+ }
+}
+
+void NativeThemeLinux::PaintCheckbox(skia::PlatformCanvas* canvas,
+ State state,
+ const gfx::Rect& rect,
+ const ButtonExtraParams& button) {
+ static SkBitmap* image_disabled_indeterminate = GfxGetBitmapNamed(
+ IDR_LINUX_CHECKBOX_DISABLED_INDETERMINATE);
+ static SkBitmap* image_indeterminate = GfxGetBitmapNamed(
+ IDR_LINUX_CHECKBOX_INDETERMINATE);
+ static SkBitmap* image_disabled_on = GfxGetBitmapNamed(
+ IDR_LINUX_CHECKBOX_DISABLED_ON);
+ static SkBitmap* image_on = GfxGetBitmapNamed(IDR_LINUX_CHECKBOX_ON);
+ static SkBitmap* image_disabled_off = GfxGetBitmapNamed(
+ IDR_LINUX_CHECKBOX_DISABLED_OFF);
+ static SkBitmap* image_off = GfxGetBitmapNamed(IDR_LINUX_CHECKBOX_OFF);
+
+ SkBitmap* image = NULL;
+ if (button.indeterminate) {
+ image = state == kDisabled ? image_disabled_indeterminate
+ : image_indeterminate;
+ } else if (button.checked) {
+ image = state == kDisabled ? image_disabled_on : image_on;
+ } else {
+ image = state == kDisabled ? image_disabled_off : image_off;
+ }
+
+ gfx::Rect bounds = rect.Center(gfx::Size(image->width(), image->height()));
+ DrawBitmapInt(canvas, *image, 0, 0, image->width(), image->height(),
+ bounds.x(), bounds.y(), bounds.width(), bounds.height());
+}
+
+void NativeThemeLinux::PaintRadio(skia::PlatformCanvas* canvas,
+ State state,
+ const gfx::Rect& rect,
+ const ButtonExtraParams& button) {
+ static SkBitmap* image_disabled_on = GfxGetBitmapNamed(
+ IDR_LINUX_RADIO_DISABLED_ON);
+ static SkBitmap* image_on = GfxGetBitmapNamed(IDR_LINUX_RADIO_ON);
+ static SkBitmap* image_disabled_off = GfxGetBitmapNamed(
+ IDR_LINUX_RADIO_DISABLED_OFF);
+ static SkBitmap* image_off = GfxGetBitmapNamed(IDR_LINUX_RADIO_OFF);
+
+ SkBitmap* image = NULL;
+ if (state == kDisabled)
+ image = button.checked ? image_disabled_on : image_disabled_off;
+ else
+ image = button.checked ? image_on : image_off;
+
+ gfx::Rect bounds = rect.Center(gfx::Size(image->width(), image->height()));
+ DrawBitmapInt(canvas, *image, 0, 0, image->width(), image->height(),
+ bounds.x(), bounds.y(), bounds.width(), bounds.height());
+}
+
+void NativeThemeLinux::PaintButton(skia::PlatformCanvas* canvas,
+ State state,
+ const gfx::Rect& rect,
+ const ButtonExtraParams& button) {
+ SkPaint paint;
+ SkRect skrect;
+ const int kRight = rect.right();
+ const int kBottom = rect.bottom();
+ SkColor base_color = button.background_color;
+
+ color_utils::HSL base_hsl;
+ color_utils::SkColorToHSL(base_color, &base_hsl);
+
+ // Our standard gradient is from 0xdd to 0xf8. This is the amount of
+ // increased luminance between those values.
+ SkColor light_color(BrightenColor(base_hsl, SkColorGetA(base_color), 0.105));
+
+ // If the button is too small, fallback to drawing a single, solid color
+ if (rect.width() < 5 || rect.height() < 5) {
+ paint.setColor(base_color);
+ skrect.set(rect.x(), rect.y(), kRight, kBottom);
+ canvas->drawRect(skrect, paint);
+ return;
+ }
+
+ const int kBorderAlpha = state == kHovered ? 0x80 : 0x55;
+ paint.setARGB(kBorderAlpha, 0, 0, 0);
+ canvas->drawLine(rect.x() + 1, rect.y(), kRight - 1, rect.y(), paint);
+ canvas->drawLine(kRight - 1, rect.y() + 1, kRight - 1, kBottom - 1, paint);
+ canvas->drawLine(rect.x() + 1, kBottom - 1, kRight - 1, kBottom - 1, paint);
+ canvas->drawLine(rect.x(), rect.y() + 1, rect.x(), kBottom - 1, paint);
+
+ paint.setColor(SK_ColorBLACK);
+ const int kLightEnd = state == kPressed ? 1 : 0;
+ const int kDarkEnd = !kLightEnd;
+ SkPoint gradient_bounds[2];
+ gradient_bounds[kLightEnd].set(SkIntToScalar(rect.x()),
+ SkIntToScalar(rect.y()));
+ gradient_bounds[kDarkEnd].set(SkIntToScalar(rect.x()),
+ SkIntToScalar(kBottom - 1));
+ SkColor colors[2];
+ colors[0] = light_color;
+ colors[1] = base_color;
+
+ SkShader* shader = SkGradientShader::CreateLinear(
+ gradient_bounds, colors, NULL, 2, SkShader::kClamp_TileMode, NULL);
+ paint.setStyle(SkPaint::kFill_Style);
+ paint.setShader(shader);
+ shader->unref();
+
+ skrect.set(rect.x() + 1, rect.y() + 1, kRight - 1, kBottom - 1);
+ canvas->drawRect(skrect, paint);
+
+ paint.setShader(NULL);
+ paint.setColor(BrightenColor(base_hsl, SkColorGetA(base_color), -0.0588));
+ canvas->drawPoint(rect.x() + 1, rect.y() + 1, paint);
+ canvas->drawPoint(kRight - 2, rect.y() + 1, paint);
+ canvas->drawPoint(rect.x() + 1, kBottom - 2, paint);
+ canvas->drawPoint(kRight - 2, kBottom - 2, paint);
+}
+
+void NativeThemeLinux::PaintTextField(skia::PlatformCanvas* canvas,
+ State state,
+ const gfx::Rect& rect,
+ const TextFieldExtraParams& text) {
+ // The following drawing code simulates the user-agent css border for
+ // text area and text input so that we do not break layout tests. Once we
+ // have decided the desired looks, we should update the code here and
+ // the layout test expectations.
+ SkRect bounds;
+ bounds.set(rect.x(), rect.y(), rect.right() - 1, rect.bottom() - 1);
+
+ SkPaint fill_paint;
+ fill_paint.setStyle(SkPaint::kFill_Style);
+ fill_paint.setColor(text.background_color);
+ canvas->drawRect(bounds, fill_paint);
+
+ if (text.is_text_area) {
+ // Draw text area border: 1px solid black
+ SkPaint stroke_paint;
+ fill_paint.setStyle(SkPaint::kStroke_Style);
+ fill_paint.setColor(SK_ColorBLACK);
+ canvas->drawRect(bounds, fill_paint);
+ } else {
+ // Draw text input and listbox inset border
+ // Text Input: 2px inset #eee
+ // Listbox: 1px inset #808080
+ const SkColor kLightColor = text.is_listbox ?
+ SkColorSetRGB(0x80, 0x80, 0x80) : SkColorSetRGB(0xee, 0xee, 0xee);
+ const SkColor kDarkColor = text.is_listbox ?
+ SkColorSetRGB(0x2c, 0x2c, 0x2c) : SkColorSetRGB(0x9a, 0x9a, 0x9a);
+ const int kBorderWidth = text.is_listbox ? 1 : 2;
+
+ SkPaint dark_paint;
+ dark_paint.setAntiAlias(true);
+ dark_paint.setStyle(SkPaint::kFill_Style);
+ dark_paint.setColor(kDarkColor);
+
+ SkPaint light_paint;
+ light_paint.setAntiAlias(true);
+ light_paint.setStyle(SkPaint::kFill_Style);
+ light_paint.setColor(kLightColor);
+
+ int left = rect.x();
+ int top = rect.y();
+ int right = rect.right();
+ int bottom = rect.bottom();
+
+ SkPath path;
+ path.incReserve(4);
+
+ // Top
+ path.moveTo(SkIntToScalar(left), SkIntToScalar(top));
+ path.lineTo(SkIntToScalar(left + kBorderWidth),
+ SkIntToScalar(top + kBorderWidth));
+ path.lineTo(SkIntToScalar(right - kBorderWidth),
+ SkIntToScalar(top + kBorderWidth));
+ path.lineTo(SkIntToScalar(right), SkIntToScalar(top));
+ canvas->drawPath(path, dark_paint);
+
+ // Bottom
+ path.reset();
+ path.moveTo(SkIntToScalar(left + kBorderWidth),
+ SkIntToScalar(bottom - kBorderWidth));
+ path.lineTo(SkIntToScalar(left), SkIntToScalar(bottom));
+ path.lineTo(SkIntToScalar(right), SkIntToScalar(bottom));
+ path.lineTo(SkIntToScalar(right - kBorderWidth),
+ SkIntToScalar(bottom - kBorderWidth));
+ canvas->drawPath(path, light_paint);
+
+ // Left
+ path.reset();
+ path.moveTo(SkIntToScalar(left), SkIntToScalar(top));
+ path.lineTo(SkIntToScalar(left), SkIntToScalar(bottom));
+ path.lineTo(SkIntToScalar(left + kBorderWidth),
+ SkIntToScalar(bottom - kBorderWidth));
+ path.lineTo(SkIntToScalar(left + kBorderWidth),
+ SkIntToScalar(top + kBorderWidth));
+ canvas->drawPath(path, dark_paint);
+
+ // Right
+ path.reset();
+ path.moveTo(SkIntToScalar(right - kBorderWidth),
+ SkIntToScalar(top + kBorderWidth));
+ path.lineTo(SkIntToScalar(right - kBorderWidth), SkIntToScalar(bottom));
+ path.lineTo(SkIntToScalar(right), SkIntToScalar(bottom));
+ path.lineTo(SkIntToScalar(right), SkIntToScalar(top));
+ canvas->drawPath(path, light_paint);
+ }
+}
+
+void NativeThemeLinux::PaintMenuList(skia::PlatformCanvas* canvas,
+ State state,
+ const gfx::Rect& rect,
+ const MenuListExtraParams& menu_list) {
+ ButtonExtraParams button = { 0 };
+ button.background_color = menu_list.background_color;
+ PaintButton(canvas, state, rect, button);
+
+ SkPaint paint;
+ paint.setColor(SK_ColorBLACK);
+ paint.setAntiAlias(true);
+ paint.setStyle(SkPaint::kFill_Style);
+
+ SkPath path;
+ path.moveTo(menu_list.arrow_x, menu_list.arrow_y - 3);
+ path.rLineTo(6, 0);
+ path.rLineTo(-3, 6);
+ path.close();
+ canvas->drawPath(path, paint);
+}
+
+void NativeThemeLinux::PaintSliderTrack(skia::PlatformCanvas* canvas,
+ State state,
+ const gfx::Rect& rect,
+ const SliderExtraParams& slider) {
+ const int kMidX = rect.x() + rect.width() / 2;
+ const int kMidY = rect.y() + rect.height() / 2;
+
+ SkPaint paint;
+ paint.setColor(kSliderTrackBackgroundColor);
+
+ SkRect skrect;
+ if (slider.vertical) {
+ skrect.set(std::max(rect.x(), kMidX - 2),
+ rect.y(),
+ std::min(rect.right(), kMidX + 2),
+ rect.bottom());
+ } else {
+ skrect.set(rect.x(),
+ std::max(rect.y(), kMidY - 2),
+ rect.right(),
+ std::min(rect.bottom(), kMidY + 2));
+ }
+ canvas->drawRect(skrect, paint);
+}
+
+void NativeThemeLinux::PaintSliderThumb(skia::PlatformCanvas* canvas,
+ State state,
+ const gfx::Rect& rect,
+ const SliderExtraParams& slider) {
+ const bool hovered = (state == kHovered) || slider.in_drag;
+ const int kMidX = rect.x() + rect.width() / 2;
+ const int kMidY = rect.y() + rect.height() / 2;
+
+ SkPaint paint;
+ paint.setColor(hovered ? SK_ColorWHITE : kSliderThumbLightGrey);
+
+ SkIRect skrect;
+ if (slider.vertical)
+ skrect.set(rect.x(), rect.y(), kMidX + 1, rect.bottom());
+ else
+ skrect.set(rect.x(), rect.y(), rect.right(), kMidY + 1);
+
+ canvas->drawIRect(skrect, paint);
+
+ paint.setColor(hovered ? kSliderThumbLightGrey : kSliderThumbDarkGrey);
+
+ if (slider.vertical)
+ skrect.set(kMidX + 1, rect.y(), rect.right(), rect.bottom());
+ else
+ skrect.set(rect.x(), kMidY + 1, rect.right(), rect.bottom());
+
+ canvas->drawIRect(skrect, paint);
+
+ paint.setColor(kSliderThumbBorderDarkGrey);
+ DrawBox(canvas, rect, paint);
+
+ if (rect.height() > 10 && rect.width() > 10) {
+ DrawHorizLine(canvas, kMidX - 2, kMidX + 2, kMidY, paint);
+ DrawHorizLine(canvas, kMidX - 2, kMidX + 2, kMidY - 3, paint);
+ DrawHorizLine(canvas, kMidX - 2, kMidX + 2, kMidY + 3, paint);
+ }
+}
+
+void NativeThemeLinux::PaintInnerSpinButton(skia::PlatformCanvas* canvas,
+ State state,
+ const gfx::Rect& rect,
+ const InnerSpinButtonExtraParams& spin_button) {
+ if (spin_button.read_only)
+ state = kDisabled;
+
+ State north_state = state;
+ State south_state = state;
+ if (spin_button.spin_up)
+ south_state = south_state != kDisabled ? kNormal : kDisabled;
+ else
+ north_state = north_state != kDisabled ? kNormal : kDisabled;
+
+ gfx::Rect half = rect;
+ half.set_height(rect.height() / 2);
+ PaintArrowButton(canvas, half, kScrollbarUpArrow, north_state);
+
+ half.set_y(rect.y() + rect.height() / 2);
+ PaintArrowButton(canvas, half, kScrollbarDownArrow, south_state);
+}
+
+void NativeThemeLinux::PaintProgressBar(skia::PlatformCanvas* canvas,
+ State state,
+ const gfx::Rect& rect,
+ const ProgressBarExtraParams& progress_bar) {
+ static SkBitmap* bar_image = GfxGetBitmapNamed(IDR_PROGRESS_BAR);
+ static SkBitmap* value_image = GfxGetBitmapNamed(IDR_PROGRESS_VALUE);
+ static SkBitmap* left_border_image = GfxGetBitmapNamed(
+ IDR_PROGRESS_BORDER_LEFT);
+ static SkBitmap* right_border_image = GfxGetBitmapNamed(
+ IDR_PROGRESS_BORDER_RIGHT);
+
+ double tile_scale = static_cast<double>(rect.height()) /
+ bar_image->height();
+
+ int new_tile_width = static_cast<int>(bar_image->width() * tile_scale);
+ double tile_scale_x = static_cast<double>(new_tile_width) /
+ bar_image->width();
+
+ DrawTiledImage(canvas, *bar_image, 0, 0, tile_scale_x, tile_scale,
+ rect.x(), rect.y(), rect.width(), rect.height());
+
+ if (progress_bar.value_rect_width) {
+
+ new_tile_width = static_cast<int>(value_image->width() * tile_scale);
+ tile_scale_x = static_cast<double>(new_tile_width) /
+ value_image->width();
+
+ DrawTiledImage(canvas, *value_image, 0, 0, tile_scale_x, tile_scale,
+ progress_bar.value_rect_x,
+ progress_bar.value_rect_y,
+ progress_bar.value_rect_width,
+ progress_bar.value_rect_height);
+ }
+
+ int dest_left_border_width = static_cast<int>(left_border_image->width() *
+ tile_scale);
+ SkRect dest_rect = {
+ SkIntToScalar(rect.x()),
+ SkIntToScalar(rect.y()),
+ SkIntToScalar(rect.x() + dest_left_border_width),
+ SkIntToScalar(rect.bottom())
+ };
+ canvas->drawBitmapRect(*left_border_image, NULL, dest_rect);
+
+ int dest_right_border_width = static_cast<int>(right_border_image->width() *
+ tile_scale);
+ dest_rect.set(SkIntToScalar(rect.right() - dest_right_border_width),
+ SkIntToScalar(rect.y()),
+ SkIntToScalar(rect.right()),
+ SkIntToScalar(rect.bottom()));
+ canvas->drawBitmapRect(*right_border_image, NULL, dest_rect);
+}
+
+bool NativeThemeLinux::IntersectsClipRectInt(
+ skia::PlatformCanvas* canvas, int x, int y, int w, int h) {
+ SkRect clip;
+ return canvas->getClipBounds(&clip) &&
+ clip.intersect(SkIntToScalar(x), SkIntToScalar(y), SkIntToScalar(x + w),
+ SkIntToScalar(y + h));
+}
+
+void NativeThemeLinux::DrawVertLine(SkCanvas* canvas,
+ int x,
+ int y1,
+ int y2,
+ const SkPaint& paint) const {
+ SkIRect skrect;
+ skrect.set(x, y1, x + 1, y2 + 1);
+ canvas->drawIRect(skrect, paint);
+}
+
+void NativeThemeLinux::DrawHorizLine(SkCanvas* canvas,
+ int x1,
+ int x2,
+ int y,
+ const SkPaint& paint) const {
+ SkIRect skrect;
+ skrect.set(x1, y, x2 + 1, y + 1);
+ canvas->drawIRect(skrect, paint);
+}
+
+void NativeThemeLinux::DrawBox(SkCanvas* canvas,
+ const gfx::Rect& rect,
+ const SkPaint& paint) const {
+ const int right = rect.x() + rect.width() - 1;
+ const int bottom = rect.y() + rect.height() - 1;
+ DrawHorizLine(canvas, rect.x(), right, rect.y(), paint);
+ DrawVertLine(canvas, right, rect.y(), bottom, paint);
+ DrawHorizLine(canvas, rect.x(), right, bottom, paint);
+ DrawVertLine(canvas, rect.x(), rect.y(), bottom, paint);
+}
+
+void NativeThemeLinux::DrawBitmapInt(
+ skia::PlatformCanvas* canvas, 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) {
+ 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(canvas, 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 };
+ canvas->drawBitmapRect(bitmap, &src_rect, dest_rect);
+ 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);
+
+ // The rect will be filled by the bitmap.
+ SkPaint p;
+ p.setFilterBitmap(true);
+ p.setShader(shader);
+ shader->unref();
+ canvas->drawRect(dest_rect, p);
+}
+
+void NativeThemeLinux::DrawTiledImage(SkCanvas* canvas,
+ const SkBitmap& bitmap,
+ int src_x, int src_y, double tile_scale_x, double tile_scale_y,
+ int dest_x, int dest_y, int w, int h) const {
+ SkShader* shader = SkShader::CreateBitmapShader(bitmap,
+ SkShader::kRepeat_TileMode,
+ SkShader::kRepeat_TileMode);
+ if (tile_scale_x != 1.0 || tile_scale_y != 1.0) {
+ SkMatrix shader_scale;
+ shader_scale.setScale(SkDoubleToScalar(tile_scale_x),
+ SkDoubleToScalar(tile_scale_y));
+ shader->setLocalMatrix(shader_scale);
+ }
+
+ SkPaint paint;
+ 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();
+ canvas->save();
+ canvas->translate(SkIntToScalar(dest_x - src_x),
+ SkIntToScalar(dest_y - src_y));
+ canvas->clipRect(SkRect::MakeXYWH(src_x, src_y, w, h));
+ canvas->drawPaint(paint);
+ canvas->restore();
+}
+
+SkScalar NativeThemeLinux::Clamp(SkScalar value,
+ SkScalar min,
+ SkScalar max) const {
+ return std::min(std::max(value, min), max);
+}
+
+SkColor NativeThemeLinux::SaturateAndBrighten(SkScalar* hsv,
+ SkScalar saturate_amount,
+ SkScalar brighten_amount) const {
+ SkScalar color[3];
+ color[0] = hsv[0];
+ color[1] = Clamp(hsv[1] + saturate_amount, 0.0, 1.0);
+ color[2] = Clamp(hsv[2] + brighten_amount, 0.0, 1.0);
+ return SkHSVToColor(color);
+}
+
+SkColor NativeThemeLinux::OutlineColor(SkScalar* hsv1, SkScalar* hsv2) const {
+ // GTK Theme engines have way too much control over the layout of
+ // the scrollbar. We might be able to more closely approximate its
+ // look-and-feel, if we sent whole images instead of just colors
+ // from the browser to the renderer. But even then, some themes
+ // would just break.
+ //
+ // So, instead, we don't even try to 100% replicate the look of
+ // the native scrollbar. We render our own version, but we make
+ // sure to pick colors that blend in nicely with the system GTK
+ // theme. In most cases, we can just sample a couple of pixels
+ // from the system scrollbar and use those colors to draw our
+ // scrollbar.
+ //
+ // This works fine for the track color and the overall thumb
+ // color. But it fails spectacularly for the outline color used
+ // around the thumb piece. Not all themes have a clearly defined
+ // outline. For some of them it is partially transparent, and for
+ // others the thickness is very unpredictable.
+ //
+ // So, instead of trying to approximate the system theme, we
+ // instead try to compute a reasonable looking choice based on the
+ // known color of the track and the thumb piece. This is difficult
+ // when trying to deal both with high- and low-contrast themes,
+ // and both with positive and inverted themes.
+ //
+ // The following code has been tested to look OK with all of the
+ // default GTK themes.
+ SkScalar min_diff = Clamp((hsv1[1] + hsv2[1]) * 1.2, 0.28, 0.5);
+ SkScalar diff = Clamp(fabs(hsv1[2] - hsv2[2]) / 2, min_diff, 0.5);
+
+ if (hsv1[2] + hsv2[2] > 1.0)
+ diff = -diff;
+
+ return SaturateAndBrighten(hsv2, -0.2, diff);
+}
+
+void NativeThemeLinux::SetScrollbarColors(unsigned inactive_color,
+ unsigned active_color,
+ unsigned track_color) const {
+ thumb_inactive_color_ = inactive_color;
+ thumb_active_color_ = active_color;
+ track_color_ = track_color;
+}
+
+} // namespace gfx
diff --git a/ui/gfx/native_theme_linux.h b/ui/gfx/native_theme_linux.h
new file mode 100644
index 0000000..b557dfc
--- /dev/null
+++ b/ui/gfx/native_theme_linux.h
@@ -0,0 +1,238 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_NATIVE_THEME_LINUX_H_
+#define UI_GFX_NATIVE_THEME_LINUX_H_
+
+#include "base/basictypes.h"
+#include "skia/ext/platform_canvas.h"
+
+namespace skia {
+class PlatformCanvas;
+}
+
+namespace gfx {
+class Rect;
+class Size;
+
+// Linux theming API.
+class NativeThemeLinux {
+ public:
+ // The part to be painted / sized.
+ enum Part {
+ kScrollbarDownArrow,
+ kScrollbarLeftArrow,
+ kScrollbarRightArrow,
+ kScrollbarUpArrow,
+ kScrollbarHorizontalThumb,
+ kScrollbarVerticalThumb,
+ kScrollbarHorizontalTrack,
+ kScrollbarVerticalTrack,
+ kCheckbox,
+ kRadio,
+ kPushButton,
+ kTextField,
+ kMenuList,
+ kSliderTrack,
+ kSliderThumb,
+ kInnerSpinButton,
+ kProgressBar,
+ };
+
+ // The state of the part.
+ enum State {
+ kDisabled,
+ kHovered,
+ kNormal,
+ kPressed,
+ };
+
+ // Extra data needed to draw scrollbar track correctly.
+ struct ScrollbarTrackExtraParams {
+ int track_x;
+ int track_y;
+ int track_width;
+ int track_height;
+ };
+
+ struct ButtonExtraParams {
+ bool checked;
+ bool indeterminate; // Whether the button state is indeterminate.
+ bool is_default; // Whether the button is default button.
+ SkColor background_color;
+ };
+
+ struct TextFieldExtraParams {
+ bool is_text_area;
+ bool is_listbox;
+ SkColor background_color;
+ };
+
+ struct MenuListExtraParams {
+ int arrow_x;
+ int arrow_y;
+ SkColor background_color;
+ };
+
+ struct SliderExtraParams {
+ bool vertical;
+ bool in_drag;
+ };
+
+ struct InnerSpinButtonExtraParams {
+ bool spin_up;
+ bool read_only;
+ };
+
+ struct ProgressBarExtraParams {
+ bool determinate;
+ int value_rect_x;
+ int value_rect_y;
+ int value_rect_width;
+ int value_rect_height;
+ };
+
+ union ExtraParams {
+ ScrollbarTrackExtraParams scrollbar_track;
+ ButtonExtraParams button;
+ MenuListExtraParams menu_list;
+ SliderExtraParams slider;
+ TextFieldExtraParams text_field;
+ InnerSpinButtonExtraParams inner_spin;
+ ProgressBarExtraParams progress_bar;
+ };
+
+ // Gets our singleton instance.
+ static NativeThemeLinux* instance();
+
+ // Return the size of the part.
+ virtual gfx::Size GetPartSize(Part part) const;
+ // Paint the part to the canvas.
+ virtual void Paint(skia::PlatformCanvas* canvas,
+ Part part,
+ State state,
+ const gfx::Rect& rect,
+ const ExtraParams& extra);
+ // Supports theme specific colors.
+ void SetScrollbarColors(unsigned inactive_color,
+ unsigned active_color,
+ unsigned track_color) const;
+
+ protected:
+ NativeThemeLinux();
+ virtual ~NativeThemeLinux();
+
+ // Draw the arrow. Used by scrollbar and inner spin button.
+ virtual void PaintArrowButton(
+ skia::PlatformCanvas* gc,
+ const gfx::Rect& rect,
+ Part direction,
+ State state);
+ // Paint the scrollbar track. Done before the thumb so that it can contain
+ // alpha.
+ virtual void PaintScrollbarTrack(skia::PlatformCanvas* canvas,
+ Part part,
+ State state,
+ const ScrollbarTrackExtraParams& extra_params,
+ const gfx::Rect& rect);
+ // Draw the scrollbar thumb over the track.
+ virtual void PaintScrollbarThumb(skia::PlatformCanvas* canvas,
+ Part part,
+ State state,
+ const gfx::Rect& rect);
+ // Draw the checkbox.
+ virtual void PaintCheckbox(skia::PlatformCanvas* canvas,
+ State state,
+ const gfx::Rect& rect,
+ const ButtonExtraParams& button);
+ // Draw the radio.
+ virtual void PaintRadio(skia::PlatformCanvas* canvas,
+ State state,
+ const gfx::Rect& rect,
+ const ButtonExtraParams& button);
+ // Draw the push button.
+ virtual void PaintButton(skia::PlatformCanvas* canvas,
+ State state,
+ const gfx::Rect& rect,
+ const ButtonExtraParams& button);
+ // Draw the text field.
+ virtual void PaintTextField(skia::PlatformCanvas* canvas,
+ State state,
+ const gfx::Rect& rect,
+ const TextFieldExtraParams& text);
+ // Draw the menu list.
+ virtual void PaintMenuList(skia::PlatformCanvas* canvas,
+ State state,
+ const gfx::Rect& rect,
+ const MenuListExtraParams& menu_list);
+ // Draw the slider track.
+ virtual void PaintSliderTrack(skia::PlatformCanvas* canvas,
+ State state,
+ const gfx::Rect& rect,
+ const SliderExtraParams& slider);
+ // Draw the slider thumb.
+ virtual void PaintSliderThumb(skia::PlatformCanvas* canvas,
+ State state,
+ const gfx::Rect& rect,
+ const SliderExtraParams& slider);
+ // Draw the inner spin button.
+ virtual void PaintInnerSpinButton(skia::PlatformCanvas* canvas,
+ State state,
+ const gfx::Rect& rect,
+ const InnerSpinButtonExtraParams& spin_button);
+ // Draw the progress bar.
+ virtual void PaintProgressBar(skia::PlatformCanvas* canvas,
+ State state,
+ const gfx::Rect& rect,
+ const ProgressBarExtraParams& progress_bar);
+
+ protected:
+ bool IntersectsClipRectInt(skia::PlatformCanvas* canvas,
+ int x, int y, int w, int h);
+
+ void DrawBitmapInt(skia::PlatformCanvas* canvas, 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);
+
+ void DrawTiledImage(SkCanvas* canvas,
+ const SkBitmap& bitmap,
+ int src_x, int src_y,
+ double tile_scale_x, double tile_scale_y,
+ int dest_x, int dest_y, int w, int h) const;
+
+ SkColor SaturateAndBrighten(SkScalar* hsv,
+ SkScalar saturate_amount,
+ SkScalar brighten_amount) const;
+
+ private:
+ void DrawVertLine(SkCanvas* canvas,
+ int x,
+ int y1,
+ int y2,
+ const SkPaint& paint) const;
+ void DrawHorizLine(SkCanvas* canvas,
+ int x1,
+ int x2,
+ int y,
+ const SkPaint& paint) const;
+ void DrawBox(SkCanvas* canvas,
+ const gfx::Rect& rect,
+ const SkPaint& paint) const;
+ SkScalar Clamp(SkScalar value,
+ SkScalar min,
+ SkScalar max) const;
+ SkColor OutlineColor(SkScalar* hsv1, SkScalar* hsv2) const;
+
+ static unsigned int scrollbar_width_;
+ static unsigned int button_length_;
+ static unsigned int thumb_inactive_color_;
+ static unsigned int thumb_active_color_;
+ static unsigned int track_color_;
+
+ DISALLOW_COPY_AND_ASSIGN(NativeThemeLinux);
+};
+
+} // namespace gfx
+
+#endif // UI_GFX_NATIVE_THEME_LINUX_H_
diff --git a/ui/gfx/native_theme_win.cc b/ui/gfx/native_theme_win.cc
new file mode 100644
index 0000000..b563b4f
--- /dev/null
+++ b/ui/gfx/native_theme_win.cc
@@ -0,0 +1,874 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "gfx/native_theme_win.h"
+
+#include <windows.h>
+#include <uxtheme.h>
+#include <vsstyle.h>
+#include <vssym32.h>
+
+#include "base/logging.h"
+#include "base/scoped_handle.h"
+#include "base/win/scoped_gdi_object.h"
+#include "base/win/scoped_hdc.h"
+#include "base/win/windows_version.h"
+#include "gfx/gdi_util.h"
+#include "gfx/rect.h"
+#include "skia/ext/platform_canvas.h"
+#include "skia/ext/skia_utils_win.h"
+#include "third_party/skia/include/core/SkShader.h"
+
+namespace {
+
+void SetCheckerboardShader(SkPaint* paint, const RECT& align_rect) {
+ // Create a 2x2 checkerboard pattern using the 3D face and highlight colors.
+ SkColor face = skia::COLORREFToSkColor(GetSysColor(COLOR_3DFACE));
+ SkColor highlight = skia::COLORREFToSkColor(GetSysColor(COLOR_3DHILIGHT));
+ SkColor buffer[] = { face, highlight, highlight, face };
+ // Confusing bit: we first create a temporary bitmap with our desired pattern,
+ // then copy it to another bitmap. The temporary bitmap doesn't take
+ // ownership of the pixel data, and so will point to garbage when this
+ // function returns. The copy will copy the pixel data into a place owned by
+ // the bitmap, which is in turn owned by the shader, etc., so it will live
+ // until we're done using it.
+ SkBitmap temp_bitmap;
+ temp_bitmap.setConfig(SkBitmap::kARGB_8888_Config, 2, 2);
+ temp_bitmap.setPixels(buffer);
+ SkBitmap bitmap;
+ temp_bitmap.copyTo(&bitmap, temp_bitmap.config());
+ SkShader* shader = SkShader::CreateBitmapShader(bitmap,
+ SkShader::kRepeat_TileMode,
+ SkShader::kRepeat_TileMode);
+
+ // Align the pattern with the upper corner of |align_rect|.
+ SkMatrix matrix;
+ matrix.setTranslate(SkIntToScalar(align_rect.left),
+ SkIntToScalar(align_rect.top));
+ shader->setLocalMatrix(matrix);
+ SkSafeUnref(paint->setShader(shader));
+}
+
+} // namespace
+
+namespace gfx {
+
+/* static */
+const NativeTheme* NativeTheme::instance() {
+ // The global NativeTheme instance.
+ static const NativeTheme s_native_theme;
+ return &s_native_theme;
+}
+
+NativeTheme::NativeTheme()
+ : theme_dll_(LoadLibrary(L"uxtheme.dll")),
+ draw_theme_(NULL),
+ draw_theme_ex_(NULL),
+ get_theme_color_(NULL),
+ get_theme_content_rect_(NULL),
+ get_theme_part_size_(NULL),
+ open_theme_(NULL),
+ close_theme_(NULL),
+ set_theme_properties_(NULL),
+ is_theme_active_(NULL),
+ get_theme_int_(NULL) {
+ if (theme_dll_) {
+ draw_theme_ = reinterpret_cast<DrawThemeBackgroundPtr>(
+ GetProcAddress(theme_dll_, "DrawThemeBackground"));
+ draw_theme_ex_ = reinterpret_cast<DrawThemeBackgroundExPtr>(
+ GetProcAddress(theme_dll_, "DrawThemeBackgroundEx"));
+ get_theme_color_ = reinterpret_cast<GetThemeColorPtr>(
+ GetProcAddress(theme_dll_, "GetThemeColor"));
+ get_theme_content_rect_ = reinterpret_cast<GetThemeContentRectPtr>(
+ GetProcAddress(theme_dll_, "GetThemeBackgroundContentRect"));
+ get_theme_part_size_ = reinterpret_cast<GetThemePartSizePtr>(
+ GetProcAddress(theme_dll_, "GetThemePartSize"));
+ open_theme_ = reinterpret_cast<OpenThemeDataPtr>(
+ GetProcAddress(theme_dll_, "OpenThemeData"));
+ close_theme_ = reinterpret_cast<CloseThemeDataPtr>(
+ GetProcAddress(theme_dll_, "CloseThemeData"));
+ set_theme_properties_ = reinterpret_cast<SetThemeAppPropertiesPtr>(
+ GetProcAddress(theme_dll_, "SetThemeAppProperties"));
+ is_theme_active_ = reinterpret_cast<IsThemeActivePtr>(
+ GetProcAddress(theme_dll_, "IsThemeActive"));
+ get_theme_int_ = reinterpret_cast<GetThemeIntPtr>(
+ GetProcAddress(theme_dll_, "GetThemeInt"));
+ }
+ memset(theme_handles_, 0, sizeof(theme_handles_));
+}
+
+NativeTheme::~NativeTheme() {
+ if (theme_dll_) {
+ // todo (cpu): fix this soon.
+ // CloseHandles();
+ FreeLibrary(theme_dll_);
+ }
+}
+
+HRESULT NativeTheme::PaintButton(HDC hdc,
+ int part_id,
+ int state_id,
+ int classic_state,
+ RECT* rect) const {
+ HANDLE handle = GetThemeHandle(BUTTON);
+ if (handle && draw_theme_)
+ return draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
+
+ // Draw it manually.
+ // All pressed states have both low bits set, and no other states do.
+ const bool focused = ((state_id & ETS_FOCUSED) == ETS_FOCUSED);
+ const bool pressed = ((state_id & PBS_PRESSED) == PBS_PRESSED);
+ if ((BP_PUSHBUTTON == part_id) && (pressed || focused)) {
+ // BP_PUSHBUTTON has a focus rect drawn around the outer edge, and the
+ // button itself is shrunk by 1 pixel.
+ HBRUSH brush = GetSysColorBrush(COLOR_3DDKSHADOW);
+ if (brush) {
+ FrameRect(hdc, rect, brush);
+ InflateRect(rect, -1, -1);
+ }
+ }
+ DrawFrameControl(hdc, rect, DFC_BUTTON, classic_state);
+
+ // Draw the focus rectangle (the dotted line box) only on buttons. For radio
+ // and checkboxes, we let webkit draw the focus rectangle (orange glow).
+ if ((BP_PUSHBUTTON == part_id) && focused) {
+ // The focus rect is inside the button. The exact number of pixels depends
+ // on whether we're in classic mode or using uxtheme.
+ if (handle && get_theme_content_rect_) {
+ get_theme_content_rect_(handle, hdc, part_id, state_id, rect, rect);
+ } else {
+ InflateRect(rect, -GetSystemMetrics(SM_CXEDGE),
+ -GetSystemMetrics(SM_CYEDGE));
+ }
+ DrawFocusRect(hdc, rect);
+ }
+
+ return S_OK;
+}
+
+HRESULT NativeTheme::PaintDialogBackground(HDC hdc, bool active,
+ RECT* rect) const {
+ HANDLE handle = GetThemeHandle(WINDOW);
+ if (handle && draw_theme_) {
+ return draw_theme_(handle, hdc, WP_DIALOG,
+ active ? FS_ACTIVE : FS_INACTIVE, rect, NULL);
+ }
+
+ // Classic just renders a flat color background.
+ FillRect(hdc, rect, reinterpret_cast<HBRUSH>(COLOR_3DFACE + 1));
+ return S_OK;
+}
+
+HRESULT NativeTheme::PaintListBackground(HDC hdc,
+ bool enabled,
+ RECT* rect) const {
+ HANDLE handle = GetThemeHandle(LIST);
+ if (handle && draw_theme_)
+ return draw_theme_(handle, hdc, 1, TS_NORMAL, rect, NULL);
+
+ // Draw it manually.
+ HBRUSH bg_brush = GetSysColorBrush(COLOR_WINDOW);
+ FillRect(hdc, rect, bg_brush);
+ DrawEdge(hdc, rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
+ return S_OK;
+}
+
+HRESULT NativeTheme::PaintMenuArrow(ThemeName theme,
+ HDC hdc,
+ int part_id,
+ int state_id,
+ RECT* rect,
+ MenuArrowDirection arrow_direction,
+ ControlState control_state) const {
+ HANDLE handle = GetThemeHandle(MENU);
+ if (handle && draw_theme_) {
+ if (arrow_direction == RIGHT_POINTING_ARROW) {
+ return draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
+ } else {
+ // There is no way to tell the uxtheme API to draw a left pointing arrow;
+ // it doesn't have a flag equivalent to DFCS_MENUARROWRIGHT. But they
+ // are needed for RTL locales on Vista. So use a memory DC and mirror
+ // the region with GDI's StretchBlt.
+ Rect r(*rect);
+ base::win::ScopedHDC mem_dc(CreateCompatibleDC(hdc));
+ base::win::ScopedBitmap mem_bitmap(CreateCompatibleBitmap(hdc, r.width(),
+ r.height()));
+ HGDIOBJ old_bitmap = SelectObject(mem_dc, mem_bitmap);
+ // Copy and horizontally mirror the background from hdc into mem_dc. Use
+ // a negative-width source rect, starting at the rightmost pixel.
+ StretchBlt(mem_dc, 0, 0, r.width(), r.height(),
+ hdc, r.right()-1, r.y(), -r.width(), r.height(), SRCCOPY);
+ // Draw the arrow.
+ RECT theme_rect = {0, 0, r.width(), r.height()};
+ HRESULT result = draw_theme_(handle, mem_dc, part_id,
+ state_id, &theme_rect, NULL);
+ // Copy and mirror the result back into mem_dc.
+ StretchBlt(hdc, r.x(), r.y(), r.width(), r.height(),
+ mem_dc, r.width()-1, 0, -r.width(), r.height(), SRCCOPY);
+ SelectObject(mem_dc, old_bitmap);
+ return result;
+ }
+ }
+
+ // For some reason, Windows uses the name DFCS_MENUARROWRIGHT to indicate a
+ // left pointing arrow. This makes the following 'if' statement slightly
+ // counterintuitive.
+ UINT state;
+ if (arrow_direction == RIGHT_POINTING_ARROW)
+ state = DFCS_MENUARROW;
+ else
+ state = DFCS_MENUARROWRIGHT;
+ return PaintFrameControl(hdc, rect, DFC_MENU, state, control_state);
+}
+
+HRESULT NativeTheme::PaintMenuBackground(ThemeName theme,
+ HDC hdc,
+ int part_id,
+ int state_id,
+ RECT* rect) const {
+ HANDLE handle = GetThemeHandle(MENU);
+ if (handle && draw_theme_) {
+ HRESULT result = draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
+ FrameRect(hdc, rect, GetSysColorBrush(COLOR_3DSHADOW));
+ return result;
+ }
+
+ FillRect(hdc, rect, GetSysColorBrush(COLOR_MENU));
+ DrawEdge(hdc, rect, EDGE_RAISED, BF_RECT);
+ return S_OK;
+}
+
+HRESULT NativeTheme::PaintMenuCheckBackground(ThemeName theme,
+ HDC hdc,
+ int part_id,
+ int state_id,
+ RECT* rect) const {
+ HANDLE handle = GetThemeHandle(MENU);
+ if (handle && draw_theme_)
+ return draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
+ // Nothing to do for background.
+ return S_OK;
+}
+
+HRESULT NativeTheme::PaintMenuCheck(ThemeName theme,
+ HDC hdc,
+ int part_id,
+ int state_id,
+ RECT* rect,
+ ControlState control_state) const {
+ HANDLE handle = GetThemeHandle(MENU);
+ if (handle && draw_theme_) {
+ return draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
+ }
+ return PaintFrameControl(hdc, rect, DFC_MENU, DFCS_MENUCHECK, control_state);
+}
+
+HRESULT NativeTheme::PaintMenuGutter(HDC hdc,
+ int part_id,
+ int state_id,
+ RECT* rect) const {
+ HANDLE handle = GetThemeHandle(MENU);
+ if (handle && draw_theme_)
+ return draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
+ return E_NOTIMPL;
+}
+
+HRESULT NativeTheme::PaintMenuItemBackground(ThemeName theme,
+ HDC hdc,
+ int part_id,
+ int state_id,
+ bool selected,
+ RECT* rect) const {
+ HANDLE handle = GetThemeHandle(MENU);
+ if (handle && draw_theme_)
+ return draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
+ if (selected)
+ FillRect(hdc, rect, GetSysColorBrush(COLOR_HIGHLIGHT));
+ return S_OK;
+}
+
+HRESULT NativeTheme::PaintMenuList(HDC hdc,
+ int part_id,
+ int state_id,
+ int classic_state,
+ RECT* rect) const {
+ HANDLE handle = GetThemeHandle(MENULIST);
+ if (handle && draw_theme_)
+ return draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
+
+ // Draw it manually.
+ DrawFrameControl(hdc, rect, DFC_SCROLL, DFCS_SCROLLCOMBOBOX | classic_state);
+ return S_OK;
+}
+
+HRESULT NativeTheme::PaintMenuSeparator(HDC hdc,
+ int part_id,
+ int state_id,
+ RECT* rect) const {
+ HANDLE handle = GetThemeHandle(MENU);
+ if (handle && draw_theme_)
+ return draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
+ DrawEdge(hdc, rect, EDGE_ETCHED, BF_TOP);
+ return S_OK;
+}
+
+HRESULT NativeTheme::PaintScrollbarArrow(HDC hdc,
+ int state_id,
+ int classic_state,
+ RECT* rect) const {
+ HANDLE handle = GetThemeHandle(SCROLLBAR);
+ if (handle && draw_theme_)
+ return draw_theme_(handle, hdc, SBP_ARROWBTN, state_id, rect, NULL);
+
+ // Draw it manually.
+ DrawFrameControl(hdc, rect, DFC_SCROLL, classic_state);
+ return S_OK;
+}
+
+HRESULT NativeTheme::PaintScrollbarTrack(
+ HDC hdc,
+ int part_id,
+ int state_id,
+ int classic_state,
+ RECT* target_rect,
+ RECT* align_rect,
+ skia::PlatformCanvas* canvas) const {
+ HANDLE handle = GetThemeHandle(SCROLLBAR);
+ if (handle && draw_theme_)
+ return draw_theme_(handle, hdc, part_id, state_id, target_rect, NULL);
+
+ // Draw it manually.
+ const DWORD colorScrollbar = GetSysColor(COLOR_SCROLLBAR);
+ const DWORD color3DFace = GetSysColor(COLOR_3DFACE);
+ if ((colorScrollbar != color3DFace) &&
+ (colorScrollbar != GetSysColor(COLOR_WINDOW))) {
+ FillRect(hdc, target_rect, reinterpret_cast<HBRUSH>(COLOR_SCROLLBAR + 1));
+ } else {
+ SkPaint paint;
+ SetCheckerboardShader(&paint, *align_rect);
+ canvas->drawIRect(skia::RECTToSkIRect(*target_rect), paint);
+ }
+ if (classic_state & DFCS_PUSHED)
+ InvertRect(hdc, target_rect);
+ return S_OK;
+}
+
+HRESULT NativeTheme::PaintScrollbarThumb(HDC hdc,
+ int part_id,
+ int state_id,
+ int classic_state,
+ RECT* rect) const {
+ HANDLE handle = GetThemeHandle(SCROLLBAR);
+ if (handle && draw_theme_)
+ return draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
+
+ // Draw it manually.
+ if ((part_id == SBP_THUMBBTNHORZ) || (part_id == SBP_THUMBBTNVERT))
+ DrawEdge(hdc, rect, EDGE_RAISED, BF_RECT | BF_MIDDLE);
+ // Classic mode doesn't have a gripper.
+ return S_OK;
+}
+
+HRESULT NativeTheme::PaintSpinButton(HDC hdc,
+ int part_id,
+ int state_id,
+ int classic_state,
+ RECT* rect) const {
+ HANDLE handle = GetThemeHandle(SPIN);
+ if (handle && draw_theme_)
+ return draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
+ DrawFrameControl(hdc, rect, DFC_SCROLL, classic_state);
+ return S_OK;
+}
+
+HRESULT NativeTheme::PaintStatusGripper(HDC hdc,
+ int part_id,
+ int state_id,
+ int classic_state,
+ RECT* rect) const {
+ HANDLE handle = GetThemeHandle(STATUS);
+ if (handle && draw_theme_) {
+ // Paint the status bar gripper. There doesn't seem to be a
+ // standard gripper in Windows for the space between
+ // scrollbars. This is pretty close, but it's supposed to be
+ // painted over a status bar.
+ return draw_theme_(handle, hdc, SP_GRIPPER, 0, rect, NULL);
+ }
+
+ // Draw a windows classic scrollbar gripper.
+ DrawFrameControl(hdc, rect, DFC_SCROLL, DFCS_SCROLLSIZEGRIP);
+ return S_OK;
+}
+
+HRESULT NativeTheme::PaintTabPanelBackground(HDC hdc, RECT* rect) const {
+ HANDLE handle = GetThemeHandle(TAB);
+ if (handle && draw_theme_)
+ return draw_theme_(handle, hdc, TABP_BODY, 0, rect, NULL);
+
+ // Classic just renders a flat color background.
+ FillRect(hdc, rect, reinterpret_cast<HBRUSH>(COLOR_3DFACE + 1));
+ return S_OK;
+}
+
+HRESULT NativeTheme::PaintTrackbar(HDC hdc,
+ int part_id,
+ int state_id,
+ int classic_state,
+ RECT* rect,
+ skia::PlatformCanvas* canvas) const {
+ // Make the channel be 4 px thick in the center of the supplied rect. (4 px
+ // matches what XP does in various menus; GetThemePartSize() doesn't seem to
+ // return good values here.)
+ RECT channel_rect = *rect;
+ const int channel_thickness = 4;
+ if (part_id == TKP_TRACK) {
+ channel_rect.top +=
+ ((channel_rect.bottom - channel_rect.top - channel_thickness) / 2);
+ channel_rect.bottom = channel_rect.top + channel_thickness;
+ } else if (part_id == TKP_TRACKVERT) {
+ channel_rect.left +=
+ ((channel_rect.right - channel_rect.left - channel_thickness) / 2);
+ channel_rect.right = channel_rect.left + channel_thickness;
+ } // else this isn't actually a channel, so |channel_rect| == |rect|.
+
+ HANDLE handle = GetThemeHandle(TRACKBAR);
+ if (handle && draw_theme_)
+ return draw_theme_(handle, hdc, part_id, state_id, &channel_rect, NULL);
+
+ // Classic mode, draw it manually.
+ if ((part_id == TKP_TRACK) || (part_id == TKP_TRACKVERT)) {
+ DrawEdge(hdc, &channel_rect, EDGE_SUNKEN, BF_RECT);
+ } else if (part_id == TKP_THUMBVERT) {
+ DrawEdge(hdc, rect, EDGE_RAISED, BF_RECT | BF_SOFT | BF_MIDDLE);
+ } else {
+ // Split rect into top and bottom pieces.
+ RECT top_section = *rect;
+ RECT bottom_section = *rect;
+ top_section.bottom -= ((bottom_section.right - bottom_section.left) / 2);
+ bottom_section.top = top_section.bottom;
+ DrawEdge(hdc, &top_section, EDGE_RAISED,
+ BF_LEFT | BF_TOP | BF_RIGHT | BF_SOFT | BF_MIDDLE | BF_ADJUST);
+
+ // Split triangular piece into two diagonals.
+ RECT& left_half = bottom_section;
+ RECT right_half = bottom_section;
+ right_half.left += ((bottom_section.right - bottom_section.left) / 2);
+ left_half.right = right_half.left;
+ DrawEdge(hdc, &left_half, EDGE_RAISED,
+ BF_DIAGONAL_ENDTOPLEFT | BF_SOFT | BF_MIDDLE | BF_ADJUST);
+ DrawEdge(hdc, &right_half, EDGE_RAISED,
+ BF_DIAGONAL_ENDBOTTOMLEFT | BF_SOFT | BF_MIDDLE | BF_ADJUST);
+
+ // If the button is pressed, draw hatching.
+ if (classic_state & DFCS_PUSHED) {
+ SkPaint paint;
+ SetCheckerboardShader(&paint, *rect);
+
+ // Fill all three pieces with the pattern.
+ canvas->drawIRect(skia::RECTToSkIRect(top_section), paint);
+
+ SkScalar left_triangle_top = SkIntToScalar(left_half.top);
+ SkScalar left_triangle_right = SkIntToScalar(left_half.right);
+ SkPath left_triangle;
+ left_triangle.moveTo(SkIntToScalar(left_half.left), left_triangle_top);
+ left_triangle.lineTo(left_triangle_right, left_triangle_top);
+ left_triangle.lineTo(left_triangle_right,
+ SkIntToScalar(left_half.bottom));
+ left_triangle.close();
+ canvas->drawPath(left_triangle, paint);
+
+ SkScalar right_triangle_left = SkIntToScalar(right_half.left);
+ SkScalar right_triangle_top = SkIntToScalar(right_half.top);
+ SkPath right_triangle;
+ right_triangle.moveTo(right_triangle_left, right_triangle_top);
+ right_triangle.lineTo(SkIntToScalar(right_half.right),
+ right_triangle_top);
+ right_triangle.lineTo(right_triangle_left,
+ SkIntToScalar(right_half.bottom));
+ right_triangle.close();
+ canvas->drawPath(right_triangle, paint);
+ }
+ }
+ return S_OK;
+}
+
+// <-a->
+// [ ***** ]
+// ____ | |
+// <-a-> <------b----->
+// a: object_width
+// b: frame_width
+// *: animating object
+//
+// - the animation goes from "[" to "]" repeatedly.
+// - the animation offset is at first "|"
+//
+static int ComputeAnimationProgress(int frame_width,
+ int object_width,
+ int pixels_per_second,
+ double animated_seconds) {
+ int animation_width = frame_width + object_width;
+ double interval = static_cast<double>(animation_width) / pixels_per_second;
+ double ratio = fmod(animated_seconds, interval) / interval;
+ return static_cast<int>(animation_width * ratio) - object_width;
+}
+
+static RECT InsetRect(const RECT* rect, int size) {
+ gfx::Rect result(*rect);
+ result.Inset(size, size);
+ return result.ToRECT();
+}
+
+HRESULT NativeTheme::PaintProgressBar(HDC hdc,
+ RECT* bar_rect,
+ RECT* value_rect,
+ bool determinate,
+ double animated_seconds,
+ skia::PlatformCanvas* canvas) const {
+ // There is no documentation about the animation speed, frame-rate, nor
+ // size of moving overlay of the indeterminate progress bar.
+ // So we just observed real-world programs and guessed following parameters.
+ const int kDeteminateOverlayPixelsPerSecond = 300;
+ const int kDeteminateOverlayWidth = 120;
+ const int kIndeterminateOverlayPixelsPerSecond = 175;
+ const int kVistaIndeterminateOverlayWidth = 120;
+ const int kXPIndeterminateOverlayWidth = 55;
+ // The thickness of the bar frame inside |value_rect|
+ const int kXPBarPadding = 3;
+
+ bool pre_vista = base::win::GetVersion() < base::win::VERSION_VISTA;
+ HANDLE handle = GetThemeHandle(PROGRESS);
+ if (handle && draw_theme_ && draw_theme_ex_) {
+ draw_theme_(handle, hdc, PP_BAR, 0, bar_rect, NULL);
+
+ int bar_width = bar_rect->right - bar_rect->left;
+ if (determinate) {
+ // TODO(morrita): this RTL guess can be wrong.
+ // We should pass the direction from WebKit side.
+ bool is_rtl = (bar_rect->right == value_rect->right &&
+ bar_rect->left != value_rect->left);
+ // We should care the direction here because PP_CNUNK painting
+ // is asymmetric.
+ DTBGOPTS value_draw_options;
+ value_draw_options.dwSize = sizeof(DTBGOPTS);
+ value_draw_options.dwFlags = is_rtl ? DTBG_MIRRORDC : 0;
+ value_draw_options.rcClip = *bar_rect;
+
+ if (pre_vista) {
+ // On XP, progress bar is chunk-style and has no glossy effect.
+ // We need to shrink destination rect to fit the part inside the bar
+ // with an appropriate margin.
+ RECT shrunk_value_rect = InsetRect(value_rect, kXPBarPadding);
+ draw_theme_ex_(handle, hdc, PP_CHUNK, 0,
+ &shrunk_value_rect, &value_draw_options);
+ } else {
+ // On Vista or later, the progress bar part has a
+ // single-block value part. It also has glossy effect.
+ // And the value part has exactly same height as the bar part
+ // so we don't need to shrink the rect.
+ draw_theme_ex_(handle, hdc, PP_FILL, 0,
+ value_rect, &value_draw_options);
+
+ int dx = ComputeAnimationProgress(bar_width,
+ kDeteminateOverlayWidth,
+ kDeteminateOverlayPixelsPerSecond,
+ animated_seconds);
+ RECT overlay_rect = *value_rect;
+ overlay_rect.left += dx;
+ overlay_rect.right = overlay_rect.left + kDeteminateOverlayWidth;
+ draw_theme_(handle, hdc, PP_MOVEOVERLAY, 0, &overlay_rect, value_rect);
+ }
+ } else {
+ // A glossy overlay for indeterminate progress bar has small pause
+ // after each animation. We emulate this by adding an invisible margin
+ // the animation has to traverse.
+ int width_with_margin = bar_width + kIndeterminateOverlayPixelsPerSecond;
+ int overlay_width = pre_vista ?
+ kXPIndeterminateOverlayWidth : kVistaIndeterminateOverlayWidth;
+ int dx = ComputeAnimationProgress(width_with_margin,
+ overlay_width,
+ kIndeterminateOverlayPixelsPerSecond,
+ animated_seconds);
+ RECT overlay_rect = *bar_rect;
+ overlay_rect.left += dx;
+ overlay_rect.right = overlay_rect.left + overlay_width;
+ if (pre_vista) {
+ RECT shrunk_rect = InsetRect(&overlay_rect, kXPBarPadding);
+ RECT shrunk_bar_rect = InsetRect(bar_rect, kXPBarPadding);
+ draw_theme_(handle, hdc, PP_CHUNK, 0, &shrunk_rect, &shrunk_bar_rect);
+ } else {
+ draw_theme_(handle, hdc, PP_MOVEOVERLAY, 0, &overlay_rect, bar_rect);
+ }
+ }
+
+ return S_OK;
+ }
+
+ HBRUSH bg_brush = GetSysColorBrush(COLOR_BTNFACE);
+ HBRUSH fg_brush = GetSysColorBrush(COLOR_BTNSHADOW);
+ FillRect(hdc, bar_rect, bg_brush);
+ FillRect(hdc, value_rect, fg_brush);
+ DrawEdge(hdc, bar_rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
+ return S_OK;
+}
+
+HRESULT NativeTheme::PaintTextField(HDC hdc,
+ int part_id,
+ int state_id,
+ int classic_state,
+ RECT* rect,
+ COLORREF color,
+ bool fill_content_area,
+ bool draw_edges) const {
+ // TODO(ojan): http://b/1210017 Figure out how to give the ability to
+ // exclude individual edges from being drawn.
+
+ HANDLE handle = GetThemeHandle(TEXTFIELD);
+ // TODO(mpcomplete): can we detect if the color is specified by the user,
+ // and if not, just use the system color?
+ // CreateSolidBrush() accepts a RGB value but alpha must be 0.
+ HBRUSH bg_brush = CreateSolidBrush(color);
+ HRESULT hr;
+ // DrawThemeBackgroundEx was introduced in XP SP2, so that it's possible
+ // draw_theme_ex_ is NULL and draw_theme_ is non-null.
+ if (handle && (draw_theme_ex_ || (draw_theme_ && draw_edges))) {
+ if (draw_theme_ex_) {
+ static DTBGOPTS omit_border_options = {
+ sizeof(DTBGOPTS),
+ DTBG_OMITBORDER,
+ {0,0,0,0}
+ };
+ DTBGOPTS* draw_opts = draw_edges ? NULL : &omit_border_options;
+ hr = draw_theme_ex_(handle, hdc, part_id, state_id, rect, draw_opts);
+ } else {
+ hr = draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
+ }
+
+ // TODO(maruel): Need to be fixed if get_theme_content_rect_ is NULL.
+ if (fill_content_area && get_theme_content_rect_) {
+ RECT content_rect;
+ hr = get_theme_content_rect_(handle, hdc, part_id, state_id, rect,
+ &content_rect);
+ FillRect(hdc, &content_rect, bg_brush);
+ }
+ } else {
+ // Draw it manually.
+ if (draw_edges)
+ DrawEdge(hdc, rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
+
+ if (fill_content_area) {
+ FillRect(hdc, rect, (classic_state & DFCS_INACTIVE) ?
+ reinterpret_cast<HBRUSH>(COLOR_BTNFACE + 1) : bg_brush);
+ }
+ hr = S_OK;
+ }
+ DeleteObject(bg_brush);
+ return hr;
+}
+
+bool NativeTheme::IsThemingActive() const {
+ if (is_theme_active_)
+ return !!is_theme_active_();
+ return false;
+}
+
+HRESULT NativeTheme::GetThemePartSize(ThemeName theme_name,
+ HDC hdc,
+ int part_id,
+ int state_id,
+ RECT* rect,
+ int ts,
+ SIZE* size) const {
+ HANDLE handle = GetThemeHandle(theme_name);
+ if (handle && get_theme_part_size_)
+ return get_theme_part_size_(handle, hdc, part_id, state_id, rect, ts, size);
+
+ return E_NOTIMPL;
+}
+
+HRESULT NativeTheme::GetThemeColor(ThemeName theme,
+ int part_id,
+ int state_id,
+ int prop_id,
+ SkColor* color) const {
+ HANDLE handle = GetThemeHandle(theme);
+ if (handle && get_theme_color_) {
+ COLORREF color_ref;
+ if (get_theme_color_(handle, part_id, state_id, prop_id, &color_ref) ==
+ S_OK) {
+ *color = skia::COLORREFToSkColor(color_ref);
+ return S_OK;
+ }
+ }
+ return E_NOTIMPL;
+}
+
+SkColor NativeTheme::GetThemeColorWithDefault(ThemeName theme,
+ int part_id,
+ int state_id,
+ int prop_id,
+ int default_sys_color) const {
+ SkColor color;
+ if (GetThemeColor(theme, part_id, state_id, prop_id, &color) != S_OK)
+ color = skia::COLORREFToSkColor(GetSysColor(default_sys_color));
+ return color;
+}
+
+HRESULT NativeTheme::GetThemeInt(ThemeName theme,
+ int part_id,
+ int state_id,
+ int prop_id,
+ int *value) const {
+ HANDLE handle = GetThemeHandle(theme);
+ if (handle && get_theme_int_)
+ return get_theme_int_(handle, part_id, state_id, prop_id, value);
+ return E_NOTIMPL;
+}
+
+Size NativeTheme::GetThemeBorderSize(ThemeName theme) const {
+ // For simplicity use the wildcard state==0, part==0, since it works
+ // for the cases we currently depend on.
+ int border;
+ if (GetThemeInt(theme, 0, 0, TMT_BORDERSIZE, &border) == S_OK)
+ return Size(border, border);
+ else
+ return Size(GetSystemMetrics(SM_CXEDGE), GetSystemMetrics(SM_CYEDGE));
+}
+
+
+void NativeTheme::DisableTheming() const {
+ if (!set_theme_properties_)
+ return;
+ set_theme_properties_(0);
+}
+
+HRESULT NativeTheme::PaintFrameControl(HDC hdc,
+ RECT* rect,
+ UINT type,
+ UINT state,
+ ControlState control_state) const {
+ const int width = rect->right - rect->left;
+ const int height = rect->bottom - rect->top;
+
+ // DrawFrameControl for menu arrow/check wants a monochrome bitmap.
+ base::win::ScopedBitmap mask_bitmap(CreateBitmap(width, height, 1, 1, NULL));
+
+ if (mask_bitmap == NULL)
+ return E_OUTOFMEMORY;
+
+ base::win::ScopedHDC bitmap_dc(CreateCompatibleDC(NULL));
+ HGDIOBJ org_bitmap = SelectObject(bitmap_dc, mask_bitmap);
+ RECT local_rect = { 0, 0, width, height };
+ DrawFrameControl(bitmap_dc, &local_rect, type, state);
+
+ // We're going to use BitBlt with a b&w mask. This results in using the dest
+ // dc's text color for the black bits in the mask, and the dest dc's
+ // background color for the white bits in the mask. DrawFrameControl draws the
+ // check in black, and the background in white.
+ int bg_color_key;
+ int text_color_key;
+ switch (control_state) {
+ case CONTROL_HIGHLIGHTED:
+ bg_color_key = COLOR_HIGHLIGHT;
+ text_color_key = COLOR_HIGHLIGHTTEXT;
+ break;
+ case CONTROL_NORMAL:
+ bg_color_key = COLOR_MENU;
+ text_color_key = COLOR_MENUTEXT;
+ break;
+ case CONTROL_DISABLED:
+ bg_color_key = COLOR_MENU;
+ text_color_key = COLOR_GRAYTEXT;
+ break;
+ default:
+ NOTREACHED();
+ bg_color_key = COLOR_MENU;
+ text_color_key = COLOR_MENUTEXT;
+ break;
+ }
+ COLORREF old_bg_color = SetBkColor(hdc, GetSysColor(bg_color_key));
+ COLORREF old_text_color = SetTextColor(hdc, GetSysColor(text_color_key));
+ BitBlt(hdc, rect->left, rect->top, width, height, bitmap_dc, 0, 0, SRCCOPY);
+ SetBkColor(hdc, old_bg_color);
+ SetTextColor(hdc, old_text_color);
+
+ SelectObject(bitmap_dc, org_bitmap);
+
+ return S_OK;
+}
+
+void NativeTheme::CloseHandles() const
+{
+ if (!close_theme_)
+ return;
+
+ for (int i = 0; i < LAST; ++i) {
+ if (theme_handles_[i])
+ close_theme_(theme_handles_[i]);
+ theme_handles_[i] = NULL;
+ }
+}
+
+bool NativeTheme::IsClassicTheme(ThemeName name) const {
+ if (!theme_dll_)
+ return true;
+
+ return !GetThemeHandle(name);
+}
+
+HANDLE NativeTheme::GetThemeHandle(ThemeName theme_name) const
+{
+ if (!open_theme_ || theme_name < 0 || theme_name >= LAST)
+ return 0;
+
+ if (theme_handles_[theme_name])
+ return theme_handles_[theme_name];
+
+ // Not found, try to load it.
+ HANDLE handle = 0;
+ switch (theme_name) {
+ case BUTTON:
+ handle = open_theme_(NULL, L"Button");
+ break;
+ case LIST:
+ handle = open_theme_(NULL, L"Listview");
+ break;
+ case MENU:
+ handle = open_theme_(NULL, L"Menu");
+ break;
+ case MENULIST:
+ handle = open_theme_(NULL, L"Combobox");
+ break;
+ case SCROLLBAR:
+ handle = open_theme_(NULL, L"Scrollbar");
+ break;
+ case STATUS:
+ handle = open_theme_(NULL, L"Status");
+ break;
+ case TAB:
+ handle = open_theme_(NULL, L"Tab");
+ break;
+ case TEXTFIELD:
+ handle = open_theme_(NULL, L"Edit");
+ break;
+ case TRACKBAR:
+ handle = open_theme_(NULL, L"Trackbar");
+ break;
+ case WINDOW:
+ handle = open_theme_(NULL, L"Window");
+ break;
+ case PROGRESS:
+ handle = open_theme_(NULL, L"Progress");
+ break;
+ case SPIN:
+ handle = open_theme_(NULL, L"Spin");
+ break;
+ default:
+ NOTREACHED();
+ }
+ theme_handles_[theme_name] = handle;
+ return handle;
+}
+
+} // namespace gfx
diff --git a/ui/gfx/native_theme_win.h b/ui/gfx/native_theme_win.h
new file mode 100644
index 0000000..ff94e52
--- /dev/null
+++ b/ui/gfx/native_theme_win.h
@@ -0,0 +1,321 @@
+// 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.
+//
+// A wrapper class for working with custom XP/Vista themes provided in
+// uxtheme.dll. This is a singleton class that can be grabbed using
+// NativeTheme::instance().
+// For more information on visual style parts and states, see:
+// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/commctls/userex/topics/partsandstates.asp
+
+#ifndef UI_GFX_NATIVE_THEME_WIN_H_
+#define UI_GFX_NATIVE_THEME_WIN_H_
+#pragma once
+
+#include <windows.h>
+#include <uxtheme.h>
+#include "base/basictypes.h"
+#include "gfx/size.h"
+#include "third_party/skia/include/core/SkColor.h"
+
+namespace skia {
+class PlatformCanvas;
+} // namespace skia
+
+namespace gfx {
+
+// TODO: Define class member enums to replace part_id and state_id parameters
+// that are currently defined in <vssym32.h>. Afterward, classic_state should
+// be removed and class users wouldn't need to include <vssym32.h> anymore.
+// This would enable HOT state on non-themed UI (like when RDP'ing) and would
+// simplify usage.
+// TODO: This class should probably be changed to be platform independent at
+// the same time.
+class NativeTheme {
+ public:
+ enum ThemeName {
+ BUTTON,
+ LIST,
+ MENU,
+ MENULIST,
+ SCROLLBAR,
+ STATUS,
+ TAB,
+ TEXTFIELD,
+ TRACKBAR,
+ WINDOW,
+ PROGRESS,
+ SPIN,
+ LAST
+ };
+
+ // This enumeration is used within PaintMenuArrow in order to indicate the
+ // direction the menu arrow should point to.
+ enum MenuArrowDirection {
+ LEFT_POINTING_ARROW,
+ RIGHT_POINTING_ARROW
+ };
+
+ enum ControlState {
+ CONTROL_NORMAL,
+ CONTROL_HIGHLIGHTED,
+ CONTROL_DISABLED
+ };
+
+ typedef HRESULT (WINAPI* DrawThemeBackgroundPtr)(HANDLE theme,
+ HDC hdc,
+ int part_id,
+ int state_id,
+ const RECT* rect,
+ const RECT* clip_rect);
+ typedef HRESULT (WINAPI* DrawThemeBackgroundExPtr)(HANDLE theme,
+ HDC hdc,
+ int part_id,
+ int state_id,
+ const RECT* rect,
+ const DTBGOPTS* opts);
+ typedef HRESULT (WINAPI* GetThemeColorPtr)(HANDLE hTheme,
+ int part_id,
+ int state_id,
+ int prop_id,
+ COLORREF* color);
+ typedef HRESULT (WINAPI* GetThemeContentRectPtr)(HANDLE hTheme,
+ HDC hdc,
+ int part_id,
+ int state_id,
+ const RECT* rect,
+ RECT* content_rect);
+ typedef HRESULT (WINAPI* GetThemePartSizePtr)(HANDLE hTheme,
+ HDC hdc,
+ int part_id,
+ int state_id,
+ RECT* rect,
+ int ts,
+ SIZE* size);
+ typedef HANDLE (WINAPI* OpenThemeDataPtr)(HWND window,
+ LPCWSTR class_list);
+ typedef HRESULT (WINAPI* CloseThemeDataPtr)(HANDLE theme);
+
+ typedef void (WINAPI* SetThemeAppPropertiesPtr) (DWORD flags);
+ typedef BOOL (WINAPI* IsThemeActivePtr)();
+ typedef HRESULT (WINAPI* GetThemeIntPtr)(HANDLE hTheme,
+ int part_id,
+ int state_id,
+ int prop_id,
+ int *value);
+
+ HRESULT PaintButton(HDC hdc,
+ int part_id,
+ int state_id,
+ int classic_state,
+ RECT* rect) const;
+
+ HRESULT PaintDialogBackground(HDC dc, bool active, RECT* rect) const;
+
+ HRESULT PaintListBackground(HDC dc, bool enabled, RECT* rect) const;
+
+ // |arrow_direction| determines whether the arrow is pointing to the left or
+ // to the right. In RTL locales, sub-menus open from right to left and
+ // therefore the menu arrow should point to the left and not to the right.
+ HRESULT PaintMenuArrow(ThemeName theme,
+ HDC hdc,
+ int part_id,
+ int state_id,
+ RECT* rect,
+ MenuArrowDirection arrow_direction,
+ ControlState state) const;
+
+ HRESULT PaintMenuBackground(ThemeName theme,
+ HDC hdc,
+ int part_id,
+ int state_id,
+ RECT* rect) const;
+
+ HRESULT PaintMenuCheck(ThemeName theme,
+ HDC hdc,
+ int part_id,
+ int state_id,
+ RECT* rect,
+ ControlState state) const;
+
+ HRESULT PaintMenuCheckBackground(ThemeName theme,
+ HDC hdc,
+ int part_id,
+ int state_id,
+ RECT* rect) const;
+
+ HRESULT PaintMenuGutter(HDC hdc,
+ int part_id,
+ int state_id,
+ RECT* rect) const;
+
+ HRESULT PaintMenuItemBackground(ThemeName theme,
+ HDC hdc,
+ int part_id,
+ int state_id,
+ bool selected,
+ RECT* rect) const;
+
+ HRESULT PaintMenuList(HDC hdc,
+ int part_id,
+ int state_id,
+ int classic_state,
+ RECT* rect) const;
+
+ HRESULT PaintMenuSeparator(HDC hdc,
+ int part_id,
+ int state_id,
+ RECT* rect) const;
+
+ // Paints a scrollbar arrow. |classic_state| should have the appropriate
+ // classic part number ORed in already.
+ HRESULT PaintScrollbarArrow(HDC hdc,
+ int state_id,
+ int classic_state,
+ RECT* rect) const;
+
+ // Paints a scrollbar track section. |align_rect| is only used in classic
+ // mode, and makes sure the checkerboard pattern in |target_rect| is aligned
+ // with one presumed to be in |align_rect|.
+ HRESULT PaintScrollbarTrack(HDC hdc,
+ int part_id,
+ int state_id,
+ int classic_state,
+ RECT* target_rect,
+ RECT* align_rect,
+ skia::PlatformCanvas* canvas) const;
+
+ // Paints a scrollbar thumb or gripper.
+ HRESULT PaintScrollbarThumb(HDC hdc,
+ int part_id,
+ int state_id,
+ int classic_state,
+ RECT* rect) const;
+
+ HRESULT PaintSpinButton(HDC hdc,
+ int part_id,
+ int state_id,
+ int classic_state,
+ RECT* rect) const;
+
+ HRESULT PaintStatusGripper(HDC hdc,
+ int part_id,
+ int state_id,
+ int classic_state,
+ RECT* rect) const;
+
+ HRESULT PaintTabPanelBackground(HDC dc, RECT* rect) const;
+
+ HRESULT PaintTextField(HDC hdc,
+ int part_id,
+ int state_id,
+ int classic_state,
+ RECT* rect,
+ COLORREF color,
+ bool fill_content_area,
+ bool draw_edges) const;
+
+ HRESULT PaintTrackbar(HDC hdc,
+ int part_id,
+ int state_id,
+ int classic_state,
+ RECT* rect,
+ skia::PlatformCanvas* canvas) const;
+
+ HRESULT PaintProgressBar(HDC hdc,
+ RECT* bar_rect,
+ RECT* value_rect,
+ bool determinate,
+ double animated_seconds,
+ skia::PlatformCanvas* canvas) const;
+
+ bool IsThemingActive() const;
+
+ HRESULT GetThemePartSize(ThemeName themeName,
+ HDC hdc,
+ int part_id,
+ int state_id,
+ RECT* rect,
+ int ts,
+ SIZE* size) const;
+
+ HRESULT GetThemeColor(ThemeName theme,
+ int part_id,
+ int state_id,
+ int prop_id,
+ SkColor* color) const;
+
+ // Get the theme color if theming is enabled. If theming is unsupported
+ // for this part, use Win32's GetSysColor to find the color specified
+ // by default_sys_color.
+ SkColor GetThemeColorWithDefault(ThemeName theme,
+ int part_id,
+ int state_id,
+ int prop_id,
+ int default_sys_color) const;
+
+ HRESULT GetThemeInt(ThemeName theme,
+ int part_id,
+ int state_id,
+ int prop_id,
+ int *result) const;
+
+ // Get the thickness of the border associated with the specified theme,
+ // defaulting to GetSystemMetrics edge size if themes are disabled.
+ // In Classic Windows, borders are typically 2px; on XP+, they are 1px.
+ Size GetThemeBorderSize(ThemeName theme) const;
+
+ // Disables all theming for top-level windows in the entire process, from
+ // when this method is called until the process exits. All the other
+ // methods in this class will continue to work, but their output will ignore
+ // the user's theme. This is meant for use when running tests that require
+ // consistent visual results.
+ void DisableTheming() const;
+
+ // Closes cached theme handles so we can unload the DLL or update our UI
+ // for a theme change.
+ void CloseHandles() const;
+
+ // Returns true if classic theme is in use.
+ bool IsClassicTheme(ThemeName name) const;
+
+ // Gets our singleton instance.
+ static const NativeTheme* instance();
+
+ private:
+ NativeTheme();
+ ~NativeTheme();
+
+ HRESULT PaintFrameControl(HDC hdc,
+ RECT* rect,
+ UINT type,
+ UINT state,
+ ControlState control_state) const;
+
+ // Returns a handle to the theme data.
+ HANDLE GetThemeHandle(ThemeName theme_name) const;
+
+ // Function pointers into uxtheme.dll.
+ DrawThemeBackgroundPtr draw_theme_;
+ DrawThemeBackgroundExPtr draw_theme_ex_;
+ GetThemeColorPtr get_theme_color_;
+ GetThemeContentRectPtr get_theme_content_rect_;
+ GetThemePartSizePtr get_theme_part_size_;
+ OpenThemeDataPtr open_theme_;
+ CloseThemeDataPtr close_theme_;
+ SetThemeAppPropertiesPtr set_theme_properties_;
+ IsThemeActivePtr is_theme_active_;
+ GetThemeIntPtr get_theme_int_;
+
+ // Handle to uxtheme.dll.
+ HMODULE theme_dll_;
+
+ // A cache of open theme handles.
+ mutable HANDLE theme_handles_[LAST];
+
+ DISALLOW_COPY_AND_ASSIGN(NativeTheme);
+};
+
+} // namespace gfx
+
+#endif // UI_GFX_NATIVE_THEME_WIN_H_
diff --git a/ui/gfx/native_theme_win_unittest.cc b/ui/gfx/native_theme_win_unittest.cc
new file mode 100644
index 0000000..b087da6
--- /dev/null
+++ b/ui/gfx/native_theme_win_unittest.cc
@@ -0,0 +1,11 @@
+// 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/native_theme_win.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(NativeThemeTest, Init) {
+ ASSERT_TRUE(gfx::NativeTheme::instance() != NULL);
+}
diff --git a/ui/gfx/native_widget_types.h b/ui/gfx/native_widget_types.h
new file mode 100644
index 0000000..bc60777
--- /dev/null
+++ b/ui/gfx/native_widget_types.h
@@ -0,0 +1,176 @@
+// 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.
+
+#ifndef UI_GFX_NATIVE_WIDGET_TYPES_H_
+#define UI_GFX_NATIVE_WIDGET_TYPES_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "build/build_config.h"
+
+// This file provides cross platform typedefs for native widget types.
+// NativeWindow: this is a handle to a native, top-level window
+// NativeView: this is a handle to a native UI element. It may be the
+// same type as a NativeWindow on some platforms.
+// NativeViewId: Often, in our cross process model, we need to pass around a
+// reference to a "window". This reference will, say, be echoed back from a
+// renderer to the browser when it wishes to query its size. On Windows we
+// use an HWND for this.
+//
+// As a rule of thumb - if you're in the renderer, you should be dealing
+// with NativeViewIds. This should remind you that you shouldn't be doing
+// direct operations on platform widgets from the renderer process.
+//
+// If you're in the browser, you're probably dealing with NativeViews,
+// unless you're in the IPC layer, which will be translating between
+// NativeViewIds from the renderer and NativeViews.
+//
+// NativeEditView: a handle to a native edit-box. The Mac folks wanted this
+// specific typedef.
+//
+// NativeImage: The platform-specific image type used for drawing UI elements
+// in the browser.
+//
+// The name 'View' here meshes with OS X where the UI elements are called
+// 'views' and with our Chrome UI code where the elements are also called
+// 'views'.
+
+#if defined(OS_WIN)
+#include <windows.h> // NOLINT
+typedef struct HFONT__* HFONT;
+#elif defined(OS_MACOSX)
+struct CGContext;
+#ifdef __OBJC__
+@class NSFont;
+@class NSImage;
+@class NSView;
+@class NSWindow;
+@class NSTextField;
+#else
+class NSFont;
+class NSImage;
+class NSView;
+class NSWindow;
+class NSTextField;
+#endif // __OBJC__
+#elif defined(TOOLKIT_USES_GTK)
+typedef struct _PangoFontDescription PangoFontDescription;
+typedef struct _GdkCursor GdkCursor;
+typedef struct _GdkPixbuf GdkPixbuf;
+typedef struct _GdkRegion GdkRegion;
+typedef struct _GtkWidget GtkWidget;
+typedef struct _GtkWindow GtkWindow;
+typedef struct _cairo cairo_t;
+#endif
+class SkBitmap;
+
+namespace gfx {
+
+#if defined(OS_WIN)
+typedef HFONT NativeFont;
+typedef HWND NativeView;
+typedef HWND NativeWindow;
+typedef HWND NativeEditView;
+typedef HDC NativeDrawingContext;
+typedef HCURSOR NativeCursor;
+typedef HMENU NativeMenu;
+typedef HRGN NativeRegion;
+#elif defined(OS_MACOSX)
+typedef NSFont* NativeFont;
+typedef NSView* NativeView;
+typedef NSWindow* NativeWindow;
+typedef NSTextField* NativeEditView;
+typedef CGContext* NativeDrawingContext;
+typedef void* NativeCursor;
+typedef void* NativeMenu;
+#elif defined(USE_X11)
+typedef PangoFontDescription* NativeFont;
+typedef GtkWidget* NativeView;
+typedef GtkWindow* NativeWindow;
+typedef GtkWidget* NativeEditView;
+typedef cairo_t* NativeDrawingContext;
+typedef GdkCursor* NativeCursor;
+typedef GtkWidget* NativeMenu;
+typedef GdkRegion* NativeRegion;
+#endif
+
+#if defined(OS_MACOSX)
+typedef NSImage NativeImageType;
+#elif defined(OS_LINUX) && !defined(TOOLKIT_VIEWS)
+typedef GdkPixbuf NativeImageType;
+#else
+typedef SkBitmap NativeImageType;
+#endif
+typedef NativeImageType* NativeImage;
+
+// Note: for test_shell we're packing a pointer into the NativeViewId. So, if
+// you make it a type which is smaller than a pointer, you have to fix
+// test_shell.
+//
+// See comment at the top of the file for usage.
+typedef intptr_t NativeViewId;
+
+#if defined(OS_WIN)
+// Convert a NativeViewId to a NativeView.
+//
+// On Windows, we pass an HWND into the renderer. As stated above, the renderer
+// should not be performing operations on the view.
+static inline NativeView NativeViewFromId(NativeViewId id) {
+ return reinterpret_cast<NativeView>(id);
+}
+#define NativeViewFromIdInBrowser(x) NativeViewFromId(x)
+#elif defined(OS_POSIX)
+// On Mac and Linux, a NativeView is a pointer to an object, and is useless
+// outside the process in which it was created. NativeViewFromId should only be
+// used inside the appropriate platform ifdef outside of the browser.
+// (NativeViewFromIdInBrowser can be used everywhere in the browser.) If your
+// cross-platform design involves a call to NativeViewFromId from outside the
+// browser it will never work on Mac or Linux and is fundamentally broken.
+
+// Please do not call this from outside the browser. It won't work; the name
+// should give you a subtle hint.
+static inline NativeView NativeViewFromIdInBrowser(NativeViewId id) {
+ return reinterpret_cast<NativeView>(id);
+}
+#endif // defined(OS_POSIX)
+
+// Convert a NativeView to a NativeViewId. See the comments at the top of
+// this file.
+#if defined(OS_WIN) || defined(OS_MACOSX)
+static inline NativeViewId IdFromNativeView(NativeView view) {
+ return reinterpret_cast<NativeViewId>(view);
+}
+#elif defined(USE_X11)
+// Not inlined because it involves pulling too many headers.
+NativeViewId IdFromNativeView(NativeView view);
+#endif // defined(USE_X11)
+
+
+// PluginWindowHandle is an abstraction wrapping "the types of windows
+// used by NPAPI plugins". On Windows it's an HWND, on X it's an X
+// window id.
+#if defined(OS_WIN)
+ typedef HWND PluginWindowHandle;
+ const PluginWindowHandle kNullPluginWindow = NULL;
+#elif defined(USE_X11)
+ typedef unsigned long PluginWindowHandle;
+ const PluginWindowHandle kNullPluginWindow = 0;
+#else
+ // On OS X we don't have windowed plugins.
+ // We use a NULL/0 PluginWindowHandle in shared code to indicate there
+ // is no window present, so mirror that behavior here.
+ //
+ // The GPU plugin is currently an exception to this rule. As of this
+ // writing it uses some NPAPI infrastructure, and minimally we need
+ // to identify the plugin instance via this window handle. When the
+ // GPU plugin becomes a full-on GPU process, this typedef can be
+ // returned to a bool. For now we use a type large enough to hold a
+ // pointer on 64-bit architectures in case we need this capability.
+ typedef uint64 PluginWindowHandle;
+ const PluginWindowHandle kNullPluginWindow = 0;
+#endif
+
+} // namespace gfx
+
+#endif // UI_GFX_NATIVE_WIDGET_TYPES_H_
diff --git a/ui/gfx/native_widget_types_gtk.cc b/ui/gfx/native_widget_types_gtk.cc
new file mode 100644
index 0000000..ccf428c
--- /dev/null
+++ b/ui/gfx/native_widget_types_gtk.cc
@@ -0,0 +1,15 @@
+// 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/native_widget_types.h"
+
+#include "gfx/gtk_native_view_id_manager.h"
+
+namespace gfx {
+
+NativeViewId IdFromNativeView(NativeView view) {
+ return GtkNativeViewManager::GetInstance()->GetIdForWidget(view);
+}
+
+} // namespace gfx
diff --git a/ui/gfx/path.cc b/ui/gfx/path.cc
new file mode 100644
index 0000000..e456679
--- /dev/null
+++ b/ui/gfx/path.cc
@@ -0,0 +1,26 @@
+// 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/path.h"
+
+#include "base/logging.h"
+
+namespace gfx {
+
+Path::Path()
+ : SkPath() {
+ moveTo(0, 0);
+}
+
+Path::Path(const Point* points, size_t count) {
+ DCHECK(count > 1);
+ moveTo(SkIntToScalar(points[0].x), SkIntToScalar(points[0].y));
+ for (size_t i = 1; i < count; ++i)
+ lineTo(SkIntToScalar(points[i].x), SkIntToScalar(points[i].y));
+}
+
+Path::~Path() {
+}
+
+} // namespace gfx
diff --git a/ui/gfx/path.h b/ui/gfx/path.h
new file mode 100644
index 0000000..9202e8e
--- /dev/null
+++ b/ui/gfx/path.h
@@ -0,0 +1,57 @@
+// 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 UI_GFX_PATH_H_
+#define UI_GFX_PATH_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "gfx/native_widget_types.h"
+
+#include "third_party/skia/include/core/SkPath.h"
+
+namespace gfx {
+
+class Path : public SkPath {
+ public:
+ // Used by Path(Point,size_t) constructor.
+ struct Point {
+ int x;
+ int y;
+ };
+
+ Path();
+
+ // Creates a path populated with the specified points.
+ Path(const Point* points, size_t count);
+
+ ~Path();
+
+#if defined(OS_WIN) || defined(USE_X11)
+ // Creates a NativeRegion from the path. The caller is responsible for freeing
+ // resources used by this region. This only supports polygon paths.
+ NativeRegion CreateNativeRegion() const;
+
+ // Returns the intersection of the two regions. The caller owns the returned
+ // object.
+ static gfx::NativeRegion IntersectRegions(gfx::NativeRegion r1,
+ gfx::NativeRegion r2);
+
+ // Returns the union of the two regions. The caller owns the returned object.
+ static gfx::NativeRegion CombineRegions(gfx::NativeRegion r1,
+ gfx::NativeRegion r2);
+
+ // Returns the difference of the two regions. The caller owns the returned
+ // object.
+ static gfx::NativeRegion SubtractRegion(gfx::NativeRegion r1,
+ gfx::NativeRegion r2);
+#endif
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Path);
+};
+
+}
+
+#endif // UI_GFX_PATH_H_
diff --git a/ui/gfx/path_gtk.cc b/ui/gfx/path_gtk.cc
new file mode 100644
index 0000000..2149aad
--- /dev/null
+++ b/ui/gfx/path_gtk.cc
@@ -0,0 +1,55 @@
+// 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/path.h"
+
+#include <gdk/gdk.h>
+
+#include "base/scoped_ptr.h"
+#include "base/command_line.h"
+
+namespace gfx {
+
+GdkRegion* Path::CreateNativeRegion() const {
+ int point_count = getPoints(NULL, 0);
+ if (point_count <= 1) {
+ // NOTE: ideally this would return gdk_empty_region, but that returns a
+ // region with nothing in it.
+ return NULL;
+ }
+
+ scoped_array<SkPoint> points(new SkPoint[point_count]);
+ getPoints(points.get(), point_count);
+
+ scoped_array<GdkPoint> gdk_points(new GdkPoint[point_count]);
+ for (int i = 0; i < point_count; ++i) {
+ gdk_points[i].x = SkScalarRound(points[i].fX);
+ gdk_points[i].y = SkScalarRound(points[i].fY);
+ }
+
+ return gdk_region_polygon(gdk_points.get(), point_count, GDK_EVEN_ODD_RULE);
+}
+
+// static
+NativeRegion Path::IntersectRegions(NativeRegion r1, NativeRegion r2) {
+ GdkRegion* copy = gdk_region_copy(r1);
+ gdk_region_intersect(copy, r2);
+ return copy;
+}
+
+// static
+NativeRegion Path::CombineRegions(NativeRegion r1, NativeRegion r2) {
+ GdkRegion* copy = gdk_region_copy(r1);
+ gdk_region_union(copy, r2);
+ return copy;
+}
+
+// static
+NativeRegion Path::SubtractRegion(NativeRegion r1, NativeRegion r2) {
+ GdkRegion* copy = gdk_region_copy(r1);
+ gdk_region_subtract(copy, r2);
+ return copy;
+}
+
+} // namespace gfx
diff --git a/ui/gfx/path_win.cc b/ui/gfx/path_win.cc
new file mode 100644
index 0000000..b5f206c
--- /dev/null
+++ b/ui/gfx/path_win.cc
@@ -0,0 +1,45 @@
+// 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/path.h"
+
+#include "base/scoped_ptr.h"
+
+namespace gfx {
+
+HRGN Path::CreateNativeRegion() const {
+ int point_count = getPoints(NULL, 0);
+ scoped_array<SkPoint> points(new SkPoint[point_count]);
+ getPoints(points.get(), point_count);
+ scoped_array<POINT> windows_points(new POINT[point_count]);
+ for (int i = 0; i < point_count; ++i) {
+ windows_points[i].x = SkScalarRound(points[i].fX);
+ windows_points[i].y = SkScalarRound(points[i].fY);
+ }
+
+ return ::CreatePolygonRgn(windows_points.get(), point_count, ALTERNATE);
+}
+
+// static
+NativeRegion Path::IntersectRegions(NativeRegion r1, NativeRegion r2) {
+ HRGN dest = CreateRectRgn(0, 0, 1, 1);
+ CombineRgn(dest, r1, r2, RGN_AND);
+ return dest;
+}
+
+// static
+NativeRegion Path::CombineRegions(NativeRegion r1, NativeRegion r2) {
+ HRGN dest = CreateRectRgn(0, 0, 1, 1);
+ CombineRgn(dest, r1, r2, RGN_OR);
+ return dest;
+}
+
+// static
+NativeRegion Path::SubtractRegion(NativeRegion r1, NativeRegion r2) {
+ HRGN dest = CreateRectRgn(0, 0, 1, 1);
+ CombineRgn(dest, r1, r2, RGN_DIFF);
+ return dest;
+}
+
+} // namespace gfx
diff --git a/ui/gfx/platform_font.h b/ui/gfx/platform_font.h
new file mode 100644
index 0000000..32cba2c
--- /dev/null
+++ b/ui/gfx/platform_font.h
@@ -0,0 +1,79 @@
+// 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.
+
+#ifndef UI_GFX_PLATFORM_FONT_
+#define UI_GFX_PLATFORM_FONT_
+#pragma once
+
+#include <string>
+
+#include "base/ref_counted.h"
+#include "base/string16.h"
+#include "gfx/native_widget_types.h"
+
+namespace gfx {
+
+class Font;
+
+class PlatformFont : public base::RefCounted<PlatformFont> {
+ public:
+ // Create an appropriate PlatformFont implementation.
+ static PlatformFont* CreateDefault();
+ static PlatformFont* CreateFromFont(const Font& other);
+ static PlatformFont* CreateFromNativeFont(NativeFont native_font);
+ static PlatformFont* CreateFromNameAndSize(const string16& font_name,
+ int font_size);
+
+ // 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.
+ virtual Font DeriveFont(int size_delta, int style) const = 0;
+
+ // 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.
+ virtual int GetHeight() const = 0;
+
+ // Returns the baseline, or ascent, of the font.
+ virtual int GetBaseline() const = 0;
+
+ // Returns the average character width for the font.
+ virtual int GetAverageCharacterWidth() const = 0;
+
+ // Returns the number of horizontal pixels needed to display the specified
+ // string.
+ virtual int GetStringWidth(const string16& text) const = 0;
+
+ // Returns the expected number of horizontal pixels needed to display the
+ // specified length of characters. Call GetStringWidth() to retrieve the
+ // actual number.
+ virtual int GetExpectedTextWidth(int length) const = 0;
+
+ // Returns the style of the font.
+ virtual int GetStyle() const = 0;
+
+ // Returns the font name.
+ virtual string16 GetFontName() const = 0;
+
+ // Returns the font size in pixels.
+ virtual int GetFontSize() const = 0;
+
+ // Returns the native font handle.
+ virtual NativeFont GetNativeFont() const = 0;
+
+ protected:
+ virtual ~PlatformFont() {}
+
+ private:
+ friend class base::RefCounted<PlatformFont>;
+};
+
+} // namespace gfx
+
+#endif // UI_GFX_PLATFORM_FONT_
+
diff --git a/ui/gfx/platform_font_gtk.cc b/ui/gfx/platform_font_gtk.cc
new file mode 100644
index 0000000..82b2e06
--- /dev/null
+++ b/ui/gfx/platform_font_gtk.cc
@@ -0,0 +1,450 @@
+// 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/platform_font_gtk.h"
+
+#include <algorithm>
+#include <fontconfig/fontconfig.h>
+#include <gdk/gdk.h>
+#include <gtk/gtk.h>
+#include <map>
+#include <pango/pango.h>
+
+#include "base/logging.h"
+#include "base/string_piece.h"
+#include "base/utf_string_conversions.h"
+#include "gfx/canvas_skia.h"
+#include "gfx/font.h"
+#include "gfx/gtk_util.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";
+
+// Returns the number of pixels in a point.
+// - multiply a point size by this to get pixels ("device units")
+// - divide a pixel size by this to get points
+float GetPixelsInPoint() {
+ static float pixels_in_point = 1.0;
+ static bool determined_value = false;
+
+ if (!determined_value) {
+ // http://goo.gl/UIh5m: "This is a scale factor between points specified in
+ // a PangoFontDescription and Cairo units. The default value is 96, meaning
+ // that a 10 point font will be 13 units high. (10 * 96. / 72. = 13.3)."
+ double pango_dpi = gfx::GetPangoResolution();
+ if (pango_dpi <= 0)
+ pango_dpi = 96.0;
+ pixels_in_point = pango_dpi / 72.0; // 72 points in an inch
+ determined_value = true;
+ }
+
+ return pixels_in_point;
+}
+
+// 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.
+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;
+ }
+}
+
+// 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.
+string16 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);
+
+ string16 font_family = UTF8ToUTF16(reinterpret_cast<char*>(match_family));
+ FcPatternDestroy(match);
+ FcPatternDestroy(pattern);
+ free(family_name_copy);
+ return font_family;
+}
+
+} // namespace
+
+namespace gfx {
+
+Font* PlatformFontGtk::default_font_ = NULL;
+
+////////////////////////////////////////////////////////////////////////////////
+// PlatformFontGtk, public:
+
+PlatformFontGtk::PlatformFontGtk() {
+ 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(desc);
+ pango_font_description_free(desc);
+ g_free(font_name);
+
+ DCHECK(default_font_);
+ }
+
+ InitFromPlatformFont(
+ static_cast<PlatformFontGtk*>(default_font_->platform_font()));
+}
+
+PlatformFontGtk::PlatformFontGtk(const Font& other) {
+ InitFromPlatformFont(
+ static_cast<PlatformFontGtk*>(other.platform_font()));
+}
+
+PlatformFontGtk::PlatformFontGtk(NativeFont native_font) {
+ const char* family_name = pango_font_description_get_family(native_font);
+
+ gint size_in_pixels = 0;
+ if (pango_font_description_get_size_is_absolute(native_font)) {
+ // If the size is absolute, then it's in Pango units rather than points.
+ // There are PANGO_SCALE Pango units in a device unit (pixel).
+ size_in_pixels = pango_font_description_get_size(native_font) / PANGO_SCALE;
+ } else {
+ // Otherwise, we need to convert from points.
+ size_in_pixels =
+ pango_font_description_get_size(native_font) * GetPixelsInPoint() /
+ PANGO_SCALE;
+ }
+
+ // Find best match font for |family_name| to make sure we can get
+ // a SkTypeface for the default font.
+ // TODO(agl): remove this.
+ string16 font_family = FindBestMatchFontFamilyName(family_name);
+
+ InitWithNameAndSize(font_family, size_in_pixels);
+ int style = 0;
+ if (pango_font_description_get_weight(native_font) == PANGO_WEIGHT_BOLD) {
+ // TODO(davemoore) What should we do about other weights? We currently
+ // only support BOLD.
+ style |= gfx::Font::BOLD;
+ }
+ if (pango_font_description_get_style(native_font) == PANGO_STYLE_ITALIC) {
+ // TODO(davemoore) What about PANGO_STYLE_OBLIQUE?
+ style |= gfx::Font::ITALIC;
+ }
+ if (style != 0)
+ style_ = style;
+}
+
+PlatformFontGtk::PlatformFontGtk(const string16& font_name,
+ int font_size) {
+ InitWithNameAndSize(font_name, font_size);
+}
+
+double PlatformFontGtk::underline_position() const {
+ const_cast<PlatformFontGtk*>(this)->InitPangoMetrics();
+ return underline_position_pixels_;
+}
+
+double PlatformFontGtk::underline_thickness() const {
+ const_cast<PlatformFontGtk*>(this)->InitPangoMetrics();
+ return underline_thickness_pixels_;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// PlatformFontGtk, PlatformFont implementation:
+
+Font PlatformFontGtk::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_pixels_);
+
+ if (style == style_) {
+ // Fast path, we just use the same typeface at a different size
+ return Font(new PlatformFontGtk(typeface_,
+ font_family_,
+ font_size_pixels_ + size_delta,
+ style_));
+ }
+
+ // If the style has changed we may need to load a new face
+ int skstyle = SkTypeface::kNormal;
+ if (gfx::Font::BOLD & style)
+ skstyle |= SkTypeface::kBold;
+ if (gfx::Font::ITALIC & style)
+ skstyle |= SkTypeface::kItalic;
+
+ SkTypeface* typeface = SkTypeface::CreateFromName(
+ UTF16ToUTF8(font_family_).c_str(),
+ static_cast<SkTypeface::Style>(skstyle));
+ SkAutoUnref tf_helper(typeface);
+
+ return Font(new PlatformFontGtk(typeface,
+ font_family_,
+ font_size_pixels_ + size_delta,
+ style));
+}
+
+int PlatformFontGtk::GetHeight() const {
+ return height_pixels_;
+}
+
+int PlatformFontGtk::GetBaseline() const {
+ return ascent_pixels_;
+}
+
+int PlatformFontGtk::GetAverageCharacterWidth() const {
+ return SkScalarRound(average_width_pixels_);
+}
+
+int PlatformFontGtk::GetStringWidth(const string16& text) const {
+ int width = 0, height = 0;
+ CanvasSkia::SizeStringInt(text, Font(const_cast<PlatformFontGtk*>(this)),
+ &width, &height, gfx::Canvas::NO_ELLIPSIS);
+ return width;
+}
+
+int PlatformFontGtk::GetExpectedTextWidth(int length) const {
+ double char_width = const_cast<PlatformFontGtk*>(this)->GetAverageWidth();
+ return round(static_cast<float>(length) * char_width);
+}
+
+int PlatformFontGtk::GetStyle() const {
+ return style_;
+}
+
+string16 PlatformFontGtk::GetFontName() const {
+ return font_family_;
+}
+
+int PlatformFontGtk::GetFontSize() const {
+ return font_size_pixels_;
+}
+
+NativeFont PlatformFontGtk::GetNativeFont() const {
+ PangoFontDescription* pfd = pango_font_description_new();
+ pango_font_description_set_family(pfd, UTF16ToUTF8(GetFontName()).c_str());
+ // Set the absolute size to avoid overflowing UI elements.
+ // pango_font_description_set_absolute_size() takes a size in Pango units.
+ // There are PANGO_SCALE Pango units in one device unit. Screen output
+ // devices use pixels as their device units.
+ pango_font_description_set_absolute_size(
+ pfd, font_size_pixels_ * PANGO_SCALE);
+
+ switch (GetStyle()) {
+ 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;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// PlatformFontGtk, private:
+
+PlatformFontGtk::PlatformFontGtk(SkTypeface* typeface,
+ const string16& name,
+ int size,
+ int style) {
+ InitWithTypefaceNameSizeAndStyle(typeface, name, size, style);
+}
+
+PlatformFontGtk::~PlatformFontGtk() {}
+
+void PlatformFontGtk::InitWithNameAndSize(const string16& font_name,
+ int font_size) {
+ DCHECK_GT(font_size, 0);
+ string16 fallback;
+
+ SkTypeface* typeface = SkTypeface::CreateFromName(
+ UTF16ToUTF8(font_name).c_str(), SkTypeface::kNormal);
+ if (!typeface) {
+ // A non-scalable font such as .pcf is specified. Falls back to a default
+ // scalable font.
+ typeface = SkTypeface::CreateFromName(
+ kFallbackFontFamilyName, SkTypeface::kNormal);
+ CHECK(typeface) << "Could not find any font: "
+ << UTF16ToUTF8(font_name)
+ << ", " << kFallbackFontFamilyName;
+ fallback = UTF8ToUTF16(kFallbackFontFamilyName);
+ }
+ SkAutoUnref typeface_helper(typeface);
+
+ InitWithTypefaceNameSizeAndStyle(typeface,
+ fallback.empty() ? font_name : fallback,
+ font_size,
+ gfx::Font::NORMAL);
+}
+
+void PlatformFontGtk::InitWithTypefaceNameSizeAndStyle(
+ SkTypeface* typeface,
+ const string16& font_family,
+ int font_size,
+ int style) {
+ typeface_helper_.reset(new SkAutoUnref(typeface));
+ typeface_ = typeface;
+ typeface_->ref();
+ font_family_ = font_family;
+ font_size_pixels_ = font_size;
+ style_ = style;
+ pango_metrics_inited_ = false;
+ average_width_pixels_ = 0.0f;
+ underline_position_pixels_ = 0.0f;
+ underline_thickness_pixels_ = 0.0f;
+
+ SkPaint paint;
+ SkPaint::FontMetrics metrics;
+ PaintSetup(&paint);
+ paint.getFontMetrics(&metrics);
+
+ ascent_pixels_ = SkScalarCeil(-metrics.fAscent);
+ height_pixels_ = ascent_pixels_ + SkScalarCeil(metrics.fDescent);
+}
+
+void PlatformFontGtk::InitFromPlatformFont(const PlatformFontGtk* other) {
+ typeface_helper_.reset(new SkAutoUnref(other->typeface_));
+ typeface_ = other->typeface_;
+ typeface_->ref();
+ font_family_ = other->font_family_;
+ font_size_pixels_ = other->font_size_pixels_;
+ style_ = other->style_;
+ height_pixels_ = other->height_pixels_;
+ ascent_pixels_ = other->ascent_pixels_;
+ pango_metrics_inited_ = other->pango_metrics_inited_;
+ average_width_pixels_ = other->average_width_pixels_;
+ underline_position_pixels_ = other->underline_position_pixels_;
+ underline_thickness_pixels_ = other->underline_thickness_pixels_;
+}
+
+void PlatformFontGtk::PaintSetup(SkPaint* paint) const {
+ paint->setAntiAlias(false);
+ paint->setSubpixelText(false);
+ paint->setTextSize(font_size_pixels_);
+ paint->setTypeface(typeface_);
+ paint->setFakeBoldText((gfx::Font::BOLD & style_) && !typeface_->isBold());
+ paint->setTextSkewX((gfx::Font::ITALIC & style_) && !typeface_->isItalic() ?
+ -SK_Scalar1/4 : 0);
+}
+
+void PlatformFontGtk::InitPangoMetrics() {
+ if (!pango_metrics_inited_) {
+ pango_metrics_inited_ = true;
+ PangoFontDescription* pango_desc = GetNativeFont();
+ PangoFontMetrics* pango_metrics = GetPangoFontMetrics(pango_desc);
+
+ underline_position_pixels_ =
+ pango_font_metrics_get_underline_position(pango_metrics) /
+ 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_pixels_ /= 2;
+
+ underline_thickness_pixels_ =
+ pango_font_metrics_get_underline_thickness(pango_metrics) /
+ PANGO_SCALE;
+
+ // First get the Pango-based width (converting from Pango units to pixels).
+ double pango_width_pixels =
+ pango_font_metrics_get_approximate_char_width(pango_metrics) /
+ PANGO_SCALE;
+
+ // Yes, this is how Microsoft recommends calculating the dialog unit
+ // conversions.
+ int text_width_pixels = GetStringWidth(
+ ASCIIToUTF16("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"));
+ double dialog_units_pixels = (text_width_pixels / 26 + 1) / 2;
+ average_width_pixels_ = std::min(pango_width_pixels, dialog_units_pixels);
+ pango_font_description_free(pango_desc);
+ }
+}
+
+
+double PlatformFontGtk::GetAverageWidth() const {
+ const_cast<PlatformFontGtk*>(this)->InitPangoMetrics();
+ return average_width_pixels_;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// PlatformFont, public:
+
+// static
+PlatformFont* PlatformFont::CreateDefault() {
+ return new PlatformFontGtk;
+}
+
+// static
+PlatformFont* PlatformFont::CreateFromFont(const Font& other) {
+ return new PlatformFontGtk(other);
+}
+
+// static
+PlatformFont* PlatformFont::CreateFromNativeFont(NativeFont native_font) {
+ return new PlatformFontGtk(native_font);
+}
+
+// static
+PlatformFont* PlatformFont::CreateFromNameAndSize(const string16& font_name,
+ int font_size) {
+ return new PlatformFontGtk(font_name, font_size);
+}
+
+} // namespace gfx
diff --git a/ui/gfx/platform_font_gtk.h b/ui/gfx/platform_font_gtk.h
new file mode 100644
index 0000000..4b265dd
--- /dev/null
+++ b/ui/gfx/platform_font_gtk.h
@@ -0,0 +1,107 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_PLATFORM_FONT_GTK_
+#define UI_GFX_PLATFORM_FONT_GTK_
+#pragma once
+
+#include "base/scoped_ptr.h"
+#include "gfx/platform_font.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+
+class SkTypeface;
+class SkPaint;
+
+namespace gfx {
+
+class PlatformFontGtk : public PlatformFont {
+ public:
+ PlatformFontGtk();
+ explicit PlatformFontGtk(const Font& other);
+ explicit PlatformFontGtk(NativeFont native_font);
+ PlatformFontGtk(const string16& font_name,
+ int font_size);
+
+ // 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;
+
+ // Overridden from PlatformFont:
+ virtual Font DeriveFont(int size_delta, int style) const;
+ virtual int GetHeight() const;
+ virtual int GetBaseline() const;
+ virtual int GetAverageCharacterWidth() const;
+ virtual int GetStringWidth(const string16& text) const;
+ virtual int GetExpectedTextWidth(int length) const;
+ virtual int GetStyle() const;
+ virtual string16 GetFontName() const;
+ virtual int GetFontSize() const;
+ virtual NativeFont GetNativeFont() const;
+
+ private:
+ // Create a new instance of this object with the specified properties. Called
+ // from DeriveFont.
+ PlatformFontGtk(SkTypeface* typeface,
+ const string16& name,
+ int size,
+ int style);
+ virtual ~PlatformFontGtk();
+
+ // Initialize this object.
+ void InitWithNameAndSize(const string16& font_name, int font_size);
+ void InitWithTypefaceNameSizeAndStyle(SkTypeface* typeface,
+ const string16& name,
+ int size,
+ int style);
+ void InitFromPlatformFont(const PlatformFontGtk* other);
+
+ // Potentially slow call to get pango metrics (average width, underline info).
+ void InitPangoMetrics();
+
+ // Setup a Skia context to use the current typeface
+ void PaintSetup(SkPaint* paint) const;
+
+ // Make |this| a copy of |other|.
+ void CopyFont(const Font& other);
+
+ // The average width of a character, initialized and cached if needed.
+ double GetAverageWidth() const;
+
+ // 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.
+ string16 font_family_;
+ int font_size_pixels_;
+ int style_;
+
+ // Cached metrics, generated at construction
+ int height_pixels_;
+ int ascent_pixels_;
+
+ // The pango metrics are much more expensive so we wait until we need them
+ // to compute them.
+ bool pango_metrics_inited_;
+ double average_width_pixels_;
+ double underline_position_pixels_;
+ double underline_thickness_pixels_;
+
+ // The default font, used for the default constructor.
+ static Font* default_font_;
+};
+
+} // namespace gfx
+
+#endif // UI_GFX_PLATFORM_FONT_GTK_
diff --git a/ui/gfx/platform_font_mac.h b/ui/gfx/platform_font_mac.h
new file mode 100644
index 0000000..123400c
--- /dev/null
+++ b/ui/gfx/platform_font_mac.h
@@ -0,0 +1,57 @@
+// 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.
+
+#ifndef UI_GFX_PLATFORM_FONT_MAC_
+#define UI_GFX_PLATFORM_FONT_MAC_
+#pragma once
+
+#include "gfx/platform_font.h"
+
+namespace gfx {
+
+class PlatformFontMac : public PlatformFont {
+ public:
+ PlatformFontMac();
+ explicit PlatformFontMac(const Font& other);
+ explicit PlatformFontMac(NativeFont native_font);
+ PlatformFontMac(const string16& font_name,
+ int font_size);
+
+ // Overridden from PlatformFont:
+ virtual Font DeriveFont(int size_delta, int style) const;
+ virtual int GetHeight() const;
+ virtual int GetBaseline() const;
+ virtual int GetAverageCharacterWidth() const;
+ virtual int GetStringWidth(const string16& text) const;
+ virtual int GetExpectedTextWidth(int length) const;
+ virtual int GetStyle() const;
+ virtual string16 GetFontName() const;
+ virtual int GetFontSize() const;
+ virtual NativeFont GetNativeFont() const;
+
+ private:
+ PlatformFontMac(const string16& font_name, int font_size, int style);
+ virtual ~PlatformFontMac() {}
+
+ // Initialize the object with the specified parameters.
+ void InitWithNameSizeAndStyle(const string16& font_name,
+ int font_size,
+ int style);
+
+ // Calculate and cache the font metrics.
+ void CalculateMetrics();
+
+ string16 font_name_;
+ int font_size_;
+ int style_;
+
+ // Cached metrics, generated at construction
+ int height_;
+ int ascent_;
+ int average_width_;
+};
+
+} // namespace gfx
+
+#endif // UI_GFX_PLATFORM_FONT_MAC_
diff --git a/ui/gfx/platform_font_mac.mm b/ui/gfx/platform_font_mac.mm
new file mode 100644
index 0000000..4aa9b88
--- /dev/null
+++ b/ui/gfx/platform_font_mac.mm
@@ -0,0 +1,143 @@
+// 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/platform_font_mac.h"
+
+#include <Cocoa/Cocoa.h>
+
+#include "base/basictypes.h"
+#include "base/scoped_nsobject.h"
+#include "base/sys_string_conversions.h"
+#include "base/utf_string_conversions.h"
+#include "gfx/canvas_skia.h"
+#include "gfx/font.h"
+
+namespace gfx {
+
+////////////////////////////////////////////////////////////////////////////////
+// PlatformFontMac, public:
+
+PlatformFontMac::PlatformFontMac() {
+ font_size_ = [NSFont systemFontSize];
+ style_ = gfx::Font::NORMAL;
+ NSFont* system_font = [NSFont systemFontOfSize:font_size_];
+ font_name_ = base::SysNSStringToUTF16([system_font fontName]);
+ CalculateMetrics();
+}
+
+PlatformFontMac::PlatformFontMac(const Font& other) {
+}
+
+PlatformFontMac::PlatformFontMac(NativeFont native_font) {
+}
+
+PlatformFontMac::PlatformFontMac(const string16& font_name,
+ int font_size) {
+ InitWithNameSizeAndStyle(font_name, font_size, gfx::Font::NORMAL);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// PlatformFontMac, PlatformFont implementation:
+
+Font PlatformFontMac::DeriveFont(int size_delta, int style) const {
+ return Font(new PlatformFontMac(font_name_, font_size_ + size_delta, style));
+}
+
+int PlatformFontMac::GetHeight() const {
+ return height_;
+}
+
+int PlatformFontMac::GetBaseline() const {
+ return ascent_;
+}
+
+int PlatformFontMac::GetAverageCharacterWidth() const {
+ return average_width_;
+}
+
+int PlatformFontMac::GetStringWidth(const string16& text) const {
+ int width = 0, height = 0;
+ CanvasSkia::SizeStringInt(text, Font(const_cast<PlatformFontMac*>(this)),
+ &width, &height, gfx::Canvas::NO_ELLIPSIS);
+ return width;
+}
+
+int PlatformFontMac::GetExpectedTextWidth(int length) const {
+ return length * average_width_;
+}
+
+int PlatformFontMac::GetStyle() const {
+ return style_;
+}
+
+string16 PlatformFontMac::GetFontName() const {
+ return font_name_;
+}
+
+int PlatformFontMac::GetFontSize() const {
+ return font_size_;
+}
+
+NativeFont PlatformFontMac::GetNativeFont() 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::SysUTF16ToNSString(font_name_)
+ size:font_size_];
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// PlatformFontMac, private:
+
+PlatformFontMac::PlatformFontMac(const string16& font_name,
+ int font_size,
+ int style) {
+ InitWithNameSizeAndStyle(font_name, font_size, style);
+}
+
+void PlatformFontMac::InitWithNameSizeAndStyle(const string16& font_name,
+ int font_size,
+ int style) {
+ font_name_ = font_name;
+ font_size_ = font_size;
+ style_ = style;
+ CalculateMetrics();
+}
+
+void PlatformFontMac::CalculateMetrics() {
+ NSFont* font = GetNativeFont();
+ scoped_nsobject<NSLayoutManager> layout_manager(
+ [[NSLayoutManager alloc] init]);
+ height_ = [layout_manager defaultLineHeightForFont:font];
+ ascent_ = [font ascender];
+ average_width_ =
+ [font boundingRectForGlyph:[font glyphWithName:@"x"]].size.width;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// PlatformFont, public:
+
+// static
+PlatformFont* PlatformFont::CreateDefault() {
+ return new PlatformFontMac;
+}
+
+// static
+PlatformFont* PlatformFont::CreateFromFont(const Font& other) {
+ return new PlatformFontMac(other);
+}
+
+// static
+PlatformFont* PlatformFont::CreateFromNativeFont(NativeFont native_font) {
+ return new PlatformFontMac(native_font);
+}
+
+// static
+PlatformFont* PlatformFont::CreateFromNameAndSize(const string16& font_name,
+ int font_size) {
+ return new PlatformFontMac(font_name, font_size);
+}
+
+} // namespace gfx
+
diff --git a/ui/gfx/platform_font_win.cc b/ui/gfx/platform_font_win.cc
new file mode 100644
index 0000000..8071640
--- /dev/null
+++ b/ui/gfx/platform_font_win.cc
@@ -0,0 +1,269 @@
+// 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/platform_font_win.h"
+
+#include <windows.h>
+#include <math.h>
+
+#include <algorithm>
+
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "base/win/win_util.h"
+#include "gfx/canvas_skia.h"
+#include "gfx/font.h"
+
+namespace {
+
+// If the tmWeight field of a TEXTMETRIC structure has a value >= this, the
+// font is bold.
+const int kTextMetricWeightBold = 700;
+
+// Returns either minimum font allowed for a current locale or
+// lf_height + size_delta value.
+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 (gfx::PlatformFontWin::get_minimum_font_size_callback)
+ min_font_size = gfx::PlatformFontWin::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;
+ }
+}
+
+} // namespace
+
+namespace gfx {
+
+// static
+PlatformFontWin::HFontRef* PlatformFontWin::base_font_ref_;
+
+// static
+PlatformFontWin::AdjustFontCallback
+ PlatformFontWin::adjust_font_callback = NULL;
+PlatformFontWin::GetMinimumFontSizeCallback
+ PlatformFontWin::get_minimum_font_size_callback = NULL;
+
+////////////////////////////////////////////////////////////////////////////////
+// PlatformFontWin, public
+
+PlatformFontWin::PlatformFontWin() : font_ref_(GetBaseFontRef()) {
+}
+
+PlatformFontWin::PlatformFontWin(const Font& other) {
+ InitWithCopyOfHFONT(other.GetNativeFont());
+}
+
+PlatformFontWin::PlatformFontWin(NativeFont native_font) {
+ InitWithCopyOfHFONT(native_font);
+}
+
+PlatformFontWin::PlatformFontWin(const string16& font_name,
+ int font_size) {
+ InitWithFontNameAndSize(font_name, font_size);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// PlatformFontWin, PlatformFont implementation:
+
+Font PlatformFontWin::DeriveFont(int size_delta, int style) const {
+ LOGFONT font_info;
+ GetObject(GetNativeFont(), sizeof(LOGFONT), &font_info);
+ font_info.lfHeight = AdjustFontSize(font_info.lfHeight, size_delta);
+ font_info.lfUnderline =
+ ((style & gfx::Font::UNDERLINED) == gfx::Font::UNDERLINED);
+ font_info.lfItalic = ((style & gfx::Font::ITALIC) == gfx::Font::ITALIC);
+ font_info.lfWeight = (style & gfx::Font::BOLD) ? FW_BOLD : FW_NORMAL;
+
+ HFONT hfont = CreateFontIndirect(&font_info);
+ return Font(new PlatformFontWin(CreateHFontRef(hfont)));
+}
+
+int PlatformFontWin::GetHeight() const {
+ return font_ref_->height();
+}
+
+int PlatformFontWin::GetBaseline() const {
+ return font_ref_->baseline();
+}
+
+int PlatformFontWin::GetAverageCharacterWidth() const {
+ return font_ref_->ave_char_width();
+}
+
+int PlatformFontWin::GetStringWidth(const string16& text) const {
+ int width = 0, height = 0;
+ CanvasSkia::SizeStringInt(text, Font(const_cast<PlatformFontWin*>(this)),
+ &width, &height, gfx::Canvas::NO_ELLIPSIS);
+ return width;
+}
+
+int PlatformFontWin::GetExpectedTextWidth(int length) const {
+ return length * std::min(font_ref_->dlu_base_x(), GetAverageCharacterWidth());
+}
+
+int PlatformFontWin::GetStyle() const {
+ return font_ref_->style();
+}
+
+string16 PlatformFontWin::GetFontName() const {
+ return font_ref_->font_name();
+}
+
+int PlatformFontWin::GetFontSize() const {
+ LOGFONT font_info;
+ GetObject(font_ref_->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;
+}
+
+NativeFont PlatformFontWin::GetNativeFont() const {
+ return font_ref_->hfont();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Font, private:
+
+void PlatformFontWin::InitWithCopyOfHFONT(HFONT hfont) {
+ DCHECK(hfont);
+ LOGFONT font_info;
+ GetObject(hfont, sizeof(LOGFONT), &font_info);
+ font_ref_ = CreateHFontRef(CreateFontIndirect(&font_info));
+}
+
+void PlatformFontWin::InitWithFontNameAndSize(const string16& 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());
+ font_ref_ = CreateHFontRef(hf);
+}
+
+// static
+PlatformFontWin::HFontRef* PlatformFontWin::GetBaseFontRef() {
+ if (base_font_ref_ == NULL) {
+ NONCLIENTMETRICS metrics;
+ base::win::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_ = PlatformFontWin::CreateHFontRef(font);
+ // base_font_ref_ is global, up the ref count so it's never deleted.
+ base_font_ref_->AddRef();
+ }
+ return base_font_ref_;
+}
+
+PlatformFontWin::HFontRef* PlatformFontWin::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);
+}
+
+PlatformFontWin::PlatformFontWin(HFontRef* hfont_ref) : font_ref_(hfont_ref) {
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// PlatformFontWin::HFontRef:
+
+PlatformFontWin::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_ = string16(font_info.lfFaceName);
+}
+
+PlatformFontWin::HFontRef::~HFontRef() {
+ DeleteObject(hfont_);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// PlatformFont, public:
+
+// static
+PlatformFont* PlatformFont::CreateDefault() {
+ return new PlatformFontWin;
+}
+
+// static
+PlatformFont* PlatformFont::CreateFromFont(const Font& other) {
+ return new PlatformFontWin(other);
+}
+
+// static
+PlatformFont* PlatformFont::CreateFromNativeFont(NativeFont native_font) {
+ return new PlatformFontWin(native_font);
+}
+
+// static
+PlatformFont* PlatformFont::CreateFromNameAndSize(const string16& font_name,
+ int font_size) {
+ return new PlatformFontWin(font_name, font_size);
+}
+
+} // namespace gfx
diff --git a/ui/gfx/platform_font_win.h b/ui/gfx/platform_font_win.h
new file mode 100644
index 0000000..4b18cd1
--- /dev/null
+++ b/ui/gfx/platform_font_win.h
@@ -0,0 +1,131 @@
+// 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.
+
+#ifndef UI_GFX_PLATFORM_FONT_WIN_
+#define UI_GFX_PLATFORM_FONT_WIN_
+#pragma once
+
+#include "base/ref_counted.h"
+#include "gfx/platform_font.h"
+
+namespace gfx {
+
+class PlatformFontWin : public PlatformFont {
+ public:
+ PlatformFontWin();
+ explicit PlatformFontWin(const Font& other);
+ explicit PlatformFontWin(NativeFont native_font);
+ PlatformFontWin(const string16& font_name,
+ int font_size);
+
+ // Dialog units to pixels conversion.
+ // See http://support.microsoft.com/kb/145994 for details.
+ int horizontal_dlus_to_pixels(int dlus) const {
+ return dlus * font_ref_->dlu_base_x() / 4;
+ }
+ int vertical_dlus_to_pixels(int dlus) const {
+ 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;
+
+ // Overridden from PlatformFont:
+ virtual Font DeriveFont(int size_delta, int style) const;
+ virtual int GetHeight() const;
+ virtual int GetBaseline() const;
+ virtual int GetAverageCharacterWidth() const;
+ virtual int GetStringWidth(const string16& text) const;
+ virtual int GetExpectedTextWidth(int length) const;
+ virtual int GetStyle() const;
+ virtual string16 GetFontName() const;
+ virtual int GetFontSize() const;
+ virtual NativeFont GetNativeFont() const;
+
+ private:
+ virtual ~PlatformFontWin() {}
+
+ // 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 string16& 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_;
+ string16 font_name_;
+
+ DISALLOW_COPY_AND_ASSIGN(HFontRef);
+ };
+
+ // Initializes this object with a copy of the specified HFONT.
+ void InitWithCopyOfHFONT(HFONT hfont);
+
+ // Initializes this object with the specified font name and size.
+ void InitWithFontNameAndSize(const string16& font_name,
+ int font_size);
+
+ // 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);
+
+ // Creates a new PlatformFontWin with the specified HFontRef. Used when
+ // constructing a Font from a HFONT we don't want to copy.
+ explicit PlatformFontWin(HFontRef* hfont_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_;
+};
+
+} // namespace gfx
+
+#endif // UI_GFX_PLATFORM_FONT_WIN_
+
diff --git a/ui/gfx/point.cc b/ui/gfx/point.cc
new file mode 100644
index 0000000..d601580
--- /dev/null
+++ b/ui/gfx/point.cc
@@ -0,0 +1,56 @@
+// 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/point.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
+#include <ostream>
+
+namespace gfx {
+
+Point::Point() : x_(0), y_(0) {
+}
+
+Point::Point(int x, int y) : x_(x), y_(y) {
+}
+
+#if defined(OS_WIN)
+Point::Point(DWORD point) {
+ POINTS points = MAKEPOINTS(point);
+ x_ = points.x;
+ y_ = points.y;
+}
+
+Point::Point(const POINT& point) : x_(point.x), y_(point.y) {
+}
+
+Point& Point::operator=(const POINT& point) {
+ x_ = point.x;
+ y_ = point.y;
+ return *this;
+}
+
+POINT Point::ToPOINT() const {
+ POINT p;
+ p.x = x_;
+ p.y = y_;
+ return p;
+}
+#elif defined(OS_MACOSX)
+Point::Point(const CGPoint& point) : x_(point.x), y_(point.y) {
+}
+
+CGPoint Point::ToCGPoint() const {
+ return CGPointMake(x_, y_);
+}
+#endif
+
+std::ostream& operator<<(std::ostream& out, const gfx::Point& p) {
+ return out << p.x() << "," << p.y();
+}
+
+} // namespace gfx
diff --git a/ui/gfx/point.h b/ui/gfx/point.h
new file mode 100644
index 0000000..b208c07
--- /dev/null
+++ b/ui/gfx/point.h
@@ -0,0 +1,101 @@
+// 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.
+
+#ifndef UI_GFX_POINT_H_
+#define UI_GFX_POINT_H_
+#pragma once
+
+#include "build/build_config.h"
+
+#include <iosfwd>
+
+#if defined(OS_WIN)
+typedef unsigned long DWORD;
+typedef struct tagPOINT POINT;
+#elif defined(OS_MACOSX)
+#include <ApplicationServices/ApplicationServices.h>
+#endif
+
+namespace gfx {
+
+// A point has an x and y coordinate.
+class Point {
+ public:
+ Point();
+ Point(int x, int y);
+#if defined(OS_WIN)
+ // |point| is a DWORD value that contains a coordinate. The x-coordinate is
+ // the low-order short and the y-coordinate is the high-order short. This
+ // value is commonly acquired from GetMessagePos/GetCursorPos.
+ explicit Point(DWORD point);
+ explicit Point(const POINT& point);
+ Point& operator=(const POINT& point);
+#elif defined(OS_MACOSX)
+ explicit Point(const CGPoint& point);
+#endif
+
+ ~Point() {}
+
+ int x() const { return x_; }
+ int y() const { return y_; }
+
+ void SetPoint(int x, int y) {
+ x_ = x;
+ y_ = y;
+ }
+
+ void set_x(int x) { x_ = x; }
+ void set_y(int y) { y_ = y; }
+
+ void Offset(int delta_x, int delta_y) {
+ x_ += delta_x;
+ y_ += delta_y;
+ }
+
+ Point Add(const Point& other) const{
+ Point copy = *this;
+ copy.Offset(other.x_, other.y_);
+ return copy;
+ }
+
+ Point Subtract(const Point& other) const {
+ Point copy = *this;
+ copy.Offset(-other.x_, -other.y_);
+ return copy;
+ }
+
+ bool operator==(const Point& rhs) const {
+ return x_ == rhs.x_ && y_ == rhs.y_;
+ }
+
+ bool operator!=(const Point& rhs) const {
+ return !(*this == rhs);
+ }
+
+ // A point is less than another point if its y-value is closer
+ // to the origin. If the y-values are the same, then point with
+ // the x-value closer to the origin is considered less than the
+ // other.
+ // This comparison is required to use Points in sets, or sorted
+ // vectors.
+ bool operator<(const Point& rhs) const {
+ return (y_ == rhs.y_) ? (x_ < rhs.x_) : (y_ < rhs.y_);
+ }
+
+#if defined(OS_WIN)
+ POINT ToPOINT() const;
+#elif defined(OS_MACOSX)
+ CGPoint ToCGPoint() const;
+#endif
+
+ private:
+ int x_;
+ int y_;
+};
+
+std::ostream& operator<<(std::ostream& out, const gfx::Point& p);
+
+} // namespace gfx
+
+#endif // UI_GFX_POINT_H_
diff --git a/ui/gfx/rect.cc b/ui/gfx/rect.cc
new file mode 100644
index 0000000..a1a72cb
--- /dev/null
+++ b/ui/gfx/rect.cc
@@ -0,0 +1,256 @@
+// 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/rect.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#elif defined(OS_MACOSX)
+#include <CoreGraphics/CGGeometry.h>
+#elif defined(OS_POSIX)
+#include <gdk/gdk.h>
+#endif
+
+#include <ostream>
+
+#include "gfx/insets.h"
+
+namespace {
+
+void AdjustAlongAxis(int dst_origin, int dst_size, int* origin, int* size) {
+ if (*origin < dst_origin) {
+ *origin = dst_origin;
+ *size = std::min(dst_size, *size);
+ } else {
+ *size = std::min(dst_size, *size);
+ *origin = std::min(dst_origin + dst_size, *origin + *size) - *size;
+ }
+}
+
+} // namespace
+
+namespace gfx {
+
+Rect::Rect() {
+}
+
+Rect::Rect(int width, int height)
+ : size_(width, height) {
+}
+
+Rect::Rect(int x, int y, int width, int height)
+ : origin_(x, y), size_(width, height) {
+}
+
+Rect::Rect(const gfx::Size& size)
+ : size_(size) {
+}
+
+Rect::Rect(const gfx::Point& origin, const gfx::Size& size)
+ : origin_(origin), size_(size) {
+}
+
+#if defined(OS_WIN)
+Rect::Rect(const RECT& r)
+ : origin_(r.left, r.top) {
+ set_width(r.right - r.left);
+ set_height(r.bottom - r.top);
+}
+
+Rect& Rect::operator=(const RECT& r) {
+ origin_.SetPoint(r.left, r.top);
+ set_width(r.right - r.left);
+ set_height(r.bottom - r.top);
+ return *this;
+}
+#elif defined(OS_MACOSX)
+Rect::Rect(const CGRect& r)
+ : origin_(r.origin.x, r.origin.y) {
+ set_width(r.size.width);
+ set_height(r.size.height);
+}
+
+Rect& Rect::operator=(const CGRect& r) {
+ origin_.SetPoint(r.origin.x, r.origin.y);
+ set_width(r.size.width);
+ set_height(r.size.height);
+ return *this;
+}
+#elif defined(OS_POSIX)
+Rect::Rect(const GdkRectangle& r)
+ : origin_(r.x, r.y) {
+ set_width(r.width);
+ set_height(r.height);
+}
+
+Rect& Rect::operator=(const GdkRectangle& r) {
+ origin_.SetPoint(r.x, r.y);
+ set_width(r.width);
+ set_height(r.height);
+ return *this;
+}
+#endif
+
+void Rect::SetRect(int x, int y, int width, int height) {
+ origin_.SetPoint(x, y);
+ set_width(width);
+ set_height(height);
+}
+
+void Rect::Inset(const gfx::Insets& insets) {
+ Inset(insets.left(), insets.top(), insets.right(), insets.bottom());
+}
+
+void Rect::Inset(int left, int top, int right, int bottom) {
+ Offset(left, top);
+ set_width(std::max(width() - left - right, 0));
+ set_height(std::max(height() - top - bottom, 0));
+}
+
+void Rect::Offset(int horizontal, int vertical) {
+ origin_.Offset(horizontal, vertical);
+}
+
+bool Rect::operator==(const Rect& other) const {
+ return origin_ == other.origin_ && size_ == other.size_;
+}
+
+bool Rect::operator<(const Rect& other) const {
+ if (origin_ == other.origin_) {
+ if (width() == other.width()) {
+ return height() < other.height();
+ } else {
+ return width() < other.width();
+ }
+ } else {
+ return origin_ < other.origin_;
+ }
+}
+
+#if defined(OS_WIN)
+RECT Rect::ToRECT() const {
+ RECT r;
+ r.left = x();
+ r.right = right();
+ r.top = y();
+ r.bottom = bottom();
+ return r;
+}
+#elif defined(OS_MACOSX)
+CGRect Rect::ToCGRect() const {
+ return CGRectMake(x(), y(), width(), height());
+}
+#elif defined(OS_POSIX)
+GdkRectangle Rect::ToGdkRectangle() const {
+ GdkRectangle r = {x(), y(), width(), height()};
+ return r;
+}
+#endif
+
+bool Rect::Contains(int point_x, int point_y) const {
+ return (point_x >= x()) && (point_x < right()) &&
+ (point_y >= y()) && (point_y < bottom());
+}
+
+bool Rect::Contains(const Rect& rect) const {
+ return (rect.x() >= x() && rect.right() <= right() &&
+ rect.y() >= y() && rect.bottom() <= bottom());
+}
+
+bool Rect::Intersects(const Rect& rect) const {
+ return !(rect.x() >= right() || rect.right() <= x() ||
+ rect.y() >= bottom() || rect.bottom() <= y());
+}
+
+Rect Rect::Intersect(const Rect& rect) const {
+ int rx = std::max(x(), rect.x());
+ int ry = std::max(y(), rect.y());
+ int rr = std::min(right(), rect.right());
+ int rb = std::min(bottom(), rect.bottom());
+
+ if (rx >= rr || ry >= rb)
+ rx = ry = rr = rb = 0; // non-intersecting
+
+ return Rect(rx, ry, rr - rx, rb - ry);
+}
+
+Rect Rect::Union(const Rect& rect) const {
+ // special case empty rects...
+ if (IsEmpty())
+ return rect;
+ if (rect.IsEmpty())
+ return *this;
+
+ int rx = std::min(x(), rect.x());
+ int ry = std::min(y(), rect.y());
+ int rr = std::max(right(), rect.right());
+ int rb = std::max(bottom(), rect.bottom());
+
+ return Rect(rx, ry, rr - rx, rb - ry);
+}
+
+Rect Rect::Subtract(const Rect& rect) const {
+ // boundary cases:
+ if (!Intersects(rect))
+ return *this;
+ if (rect.Contains(*this))
+ return Rect();
+
+ int rx = x();
+ int ry = y();
+ int rr = right();
+ int rb = bottom();
+
+ if (rect.y() <= y() && rect.bottom() >= bottom()) {
+ // complete intersection in the y-direction
+ if (rect.x() <= x()) {
+ rx = rect.right();
+ } else {
+ rr = rect.x();
+ }
+ } else if (rect.x() <= x() && rect.right() >= right()) {
+ // complete intersection in the x-direction
+ if (rect.y() <= y()) {
+ ry = rect.bottom();
+ } else {
+ rb = rect.y();
+ }
+ }
+ return Rect(rx, ry, rr - rx, rb - ry);
+}
+
+Rect Rect::AdjustToFit(const Rect& rect) const {
+ int new_x = x();
+ int new_y = y();
+ int new_width = width();
+ int new_height = height();
+ AdjustAlongAxis(rect.x(), rect.width(), &new_x, &new_width);
+ AdjustAlongAxis(rect.y(), rect.height(), &new_y, &new_height);
+ return Rect(new_x, new_y, new_width, new_height);
+}
+
+Point Rect::CenterPoint() const {
+ return Point(x() + (width() + 1) / 2, y() + (height() + 1) / 2);
+}
+
+Rect Rect::Center(const gfx::Size& size) const {
+ int new_width = std::min(width(), size.width());
+ int new_height = std::min(height(), size.height());
+ int new_x = x() + (width() - new_width) / 2;
+ int new_y = y() + (height() - new_height) / 2;
+ return Rect(new_x, new_y, new_width, new_height);
+}
+
+bool Rect::SharesEdgeWith(const gfx::Rect& rect) const {
+ return (y() == rect.y() && height() == rect.height() &&
+ (x() == rect.right() || right() == rect.x())) ||
+ (x() == rect.x() && width() == rect.width() &&
+ (y() == rect.bottom() || bottom() == rect.y()));
+}
+
+std::ostream& operator<<(std::ostream& out, const gfx::Rect& r) {
+ return out << r.origin() << " " << r.size();
+}
+
+} // namespace gfx
diff --git a/ui/gfx/rect.h b/ui/gfx/rect.h
new file mode 100644
index 0000000..1a1d9e6
--- /dev/null
+++ b/ui/gfx/rect.h
@@ -0,0 +1,184 @@
+// 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.
+
+// Defines a simple integer rectangle class. The containment semantics
+// are array-like; that is, the coordinate (x, y) is considered to be
+// contained by the rectangle, but the coordinate (x + width, y) is not.
+// The class will happily let you create malformed rectangles (that is,
+// rectangles with negative width and/or height), but there will be assertions
+// in the operations (such as Contains()) to complain in this case.
+
+#ifndef UI_GFX_RECT_H_
+#define UI_GFX_RECT_H_
+#pragma once
+
+#include <iosfwd>
+
+#include "gfx/point.h"
+#include "gfx/size.h"
+
+#if defined(OS_WIN)
+typedef struct tagRECT RECT;
+#elif defined(USE_X11)
+typedef struct _GdkRectangle GdkRectangle;
+#endif
+
+namespace gfx {
+
+class Insets;
+
+class Rect {
+ public:
+ Rect();
+ Rect(int width, int height);
+ Rect(int x, int y, int width, int height);
+#if defined(OS_WIN)
+ explicit Rect(const RECT& r);
+#elif defined(OS_MACOSX)
+ explicit Rect(const CGRect& r);
+#elif defined(USE_X11)
+ explicit Rect(const GdkRectangle& r);
+#endif
+ explicit Rect(const gfx::Size& size);
+ Rect(const gfx::Point& origin, const gfx::Size& size);
+
+ ~Rect() {}
+
+#if defined(OS_WIN)
+ Rect& operator=(const RECT& r);
+#elif defined(OS_MACOSX)
+ Rect& operator=(const CGRect& r);
+#elif defined(USE_X11)
+ Rect& operator=(const GdkRectangle& r);
+#endif
+
+ int x() const { return origin_.x(); }
+ void set_x(int x) { origin_.set_x(x); }
+
+ int y() const { return origin_.y(); }
+ void set_y(int y) { origin_.set_y(y); }
+
+ int width() const { return size_.width(); }
+ void set_width(int width) { size_.set_width(width); }
+
+ int height() const { return size_.height(); }
+ void set_height(int height) { size_.set_height(height); }
+
+ const gfx::Point& origin() const { return origin_; }
+ void set_origin(const gfx::Point& origin) { origin_ = origin; }
+
+ const gfx::Size& size() const { return size_; }
+ void set_size(const gfx::Size& size) { size_ = size; }
+
+ int right() const { return x() + width(); }
+ int bottom() const { return y() + height(); }
+
+ void SetRect(int x, int y, int width, int height);
+
+ // Shrink the rectangle by a horizontal and vertical distance on all sides.
+ void Inset(int horizontal, int vertical) {
+ Inset(horizontal, vertical, horizontal, vertical);
+ }
+
+ // Shrink the rectangle by the given insets.
+ void Inset(const gfx::Insets& insets);
+
+ // Shrink the rectangle by the specified amount on each side.
+ void Inset(int left, int top, int right, int bottom);
+
+ // Move the rectangle by a horizontal and vertical distance.
+ void Offset(int horizontal, int vertical);
+ void Offset(const gfx::Point& point) {
+ Offset(point.x(), point.y());
+ }
+
+ // Returns true if the area of the rectangle is zero.
+ bool IsEmpty() const { return size_.IsEmpty(); }
+
+ bool operator==(const Rect& other) const;
+
+ bool operator!=(const Rect& other) const {
+ return !(*this == other);
+ }
+
+ // A rect is less than another rect if its origin is less than
+ // the other rect's origin. If the origins are equal, then the
+ // shortest rect is less than the other. If the origin and the
+ // height are equal, then the narrowest rect is less than.
+ // This comparison is required to use Rects in sets, or sorted
+ // vectors.
+ bool operator<(const Rect& other) const;
+
+#if defined(OS_WIN)
+ // Construct an equivalent Win32 RECT object.
+ RECT ToRECT() const;
+#elif defined(USE_X11)
+ GdkRectangle ToGdkRectangle() const;
+#elif defined(OS_MACOSX)
+ // Construct an equivalent CoreGraphics object.
+ CGRect ToCGRect() const;
+#endif
+
+ // Returns true if the point identified by point_x and point_y falls inside
+ // this rectangle. The point (x, y) is inside the rectangle, but the
+ // point (x + width, y + height) is not.
+ bool Contains(int point_x, int point_y) const;
+
+ // Returns true if the specified point is contained by this rectangle.
+ bool Contains(const gfx::Point& point) const {
+ return Contains(point.x(), point.y());
+ }
+
+ // Returns true if this rectangle contains the specified rectangle.
+ bool Contains(const Rect& rect) const;
+
+ // Returns true if this rectangle intersects the specified rectangle.
+ bool Intersects(const Rect& rect) const;
+
+ // Computes the intersection of this rectangle with the given rectangle.
+ Rect Intersect(const Rect& rect) const;
+
+ // Computes the union of this rectangle with the given rectangle. The union
+ // is the smallest rectangle containing both rectangles.
+ Rect Union(const Rect& rect) const;
+
+ // Computes the rectangle resulting from subtracting |rect| from |this|. If
+ // |rect| does not intersect completely in either the x- or y-direction, then
+ // |*this| is returned. If |rect| contains |this|, then an empty Rect is
+ // returned.
+ Rect Subtract(const Rect& rect) const;
+
+ // Returns true if this rectangle equals that of the supplied rectangle.
+ bool Equals(const Rect& rect) const {
+ return *this == rect;
+ }
+
+ // Fits as much of the receiving rectangle into the supplied rectangle as
+ // possible, returning the result. For example, if the receiver had
+ // a x-location of 2 and a width of 4, and the supplied rectangle had
+ // an x-location of 0 with a width of 5, the returned rectangle would have
+ // an x-location of 1 with a width of 4.
+ Rect AdjustToFit(const Rect& rect) const;
+
+ // Returns the center of this rectangle.
+ Point CenterPoint() const;
+
+ // Return a rectangle that has the same center point but with a size capped
+ // at given |size|.
+ Rect Center(const gfx::Size& size) const;
+
+ // Returns true if this rectangle shares an entire edge (i.e., same width or
+ // same height) with the given rectangle, and the rectangles do not overlap.
+ bool SharesEdgeWith(const gfx::Rect& rect) const;
+
+ private:
+ gfx::Point origin_;
+ gfx::Size size_;
+};
+
+std::ostream& operator<<(std::ostream& out, const gfx::Rect& r);
+
+} // namespace gfx
+
+#endif // UI_GFX_RECT_H_
diff --git a/ui/gfx/rect_unittest.cc b/ui/gfx/rect_unittest.cc
new file mode 100644
index 0000000..f5b4d9b
--- /dev/null
+++ b/ui/gfx/rect_unittest.cc
@@ -0,0 +1,314 @@
+// 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 "base/basictypes.h"
+#include "gfx/rect.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+typedef testing::Test RectTest;
+
+TEST(RectTest, Contains) {
+ static const struct ContainsCase {
+ int rect_x;
+ int rect_y;
+ int rect_width;
+ int rect_height;
+ int point_x;
+ int point_y;
+ bool contained;
+ } contains_cases[] = {
+ {0, 0, 10, 10, 0, 0, true},
+ {0, 0, 10, 10, 5, 5, true},
+ {0, 0, 10, 10, 9, 9, true},
+ {0, 0, 10, 10, 5, 10, false},
+ {0, 0, 10, 10, 10, 5, false},
+ {0, 0, 10, 10, -1, -1, false},
+ {0, 0, 10, 10, 50, 50, false},
+ #ifdef NDEBUG
+ {0, 0, -10, -10, 0, 0, false},
+ #endif // NDEBUG
+ };
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(contains_cases); ++i) {
+ const ContainsCase& value = contains_cases[i];
+ gfx::Rect rect(value.rect_x, value.rect_y,
+ value.rect_width, value.rect_height);
+ EXPECT_EQ(value.contained, rect.Contains(value.point_x, value.point_y));
+ }
+}
+
+TEST(RectTest, Intersects) {
+ static const struct {
+ int x1; // rect 1
+ int y1;
+ int w1;
+ int h1;
+ int x2; // rect 2
+ int y2;
+ int w2;
+ int h2;
+ bool intersects;
+ } tests[] = {
+ { 0, 0, 0, 0, 0, 0, 0, 0, false },
+ { 0, 0, 10, 10, 0, 0, 10, 10, true },
+ { 0, 0, 10, 10, 10, 10, 10, 10, false },
+ { 10, 10, 10, 10, 0, 0, 10, 10, false },
+ { 10, 10, 10, 10, 5, 5, 10, 10, true },
+ { 10, 10, 10, 10, 15, 15, 10, 10, true },
+ { 10, 10, 10, 10, 20, 15, 10, 10, false },
+ { 10, 10, 10, 10, 21, 15, 10, 10, false }
+ };
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
+ gfx::Rect r1(tests[i].x1, tests[i].y1, tests[i].w1, tests[i].h1);
+ gfx::Rect r2(tests[i].x2, tests[i].y2, tests[i].w2, tests[i].h2);
+ EXPECT_EQ(tests[i].intersects, r1.Intersects(r2));
+ }
+}
+
+TEST(RectTest, Intersect) {
+ static const struct {
+ int x1; // rect 1
+ int y1;
+ int w1;
+ int h1;
+ int x2; // rect 2
+ int y2;
+ int w2;
+ int h2;
+ int x3; // rect 3: the union of rects 1 and 2
+ int y3;
+ int w3;
+ int h3;
+ } tests[] = {
+ { 0, 0, 0, 0, // zeros
+ 0, 0, 0, 0,
+ 0, 0, 0, 0 },
+ { 0, 0, 4, 4, // equal
+ 0, 0, 4, 4,
+ 0, 0, 4, 4 },
+ { 0, 0, 4, 4, // neighboring
+ 4, 4, 4, 4,
+ 0, 0, 0, 0 },
+ { 0, 0, 4, 4, // overlapping corners
+ 2, 2, 4, 4,
+ 2, 2, 2, 2 },
+ { 0, 0, 4, 4, // T junction
+ 3, 1, 4, 2,
+ 3, 1, 1, 2 },
+ { 3, 0, 2, 2, // gap
+ 0, 0, 2, 2,
+ 0, 0, 0, 0 }
+ };
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
+ gfx::Rect r1(tests[i].x1, tests[i].y1, tests[i].w1, tests[i].h1);
+ gfx::Rect r2(tests[i].x2, tests[i].y2, tests[i].w2, tests[i].h2);
+ gfx::Rect r3(tests[i].x3, tests[i].y3, tests[i].w3, tests[i].h3);
+ gfx::Rect ir = r1.Intersect(r2);
+ EXPECT_EQ(r3.x(), ir.x());
+ EXPECT_EQ(r3.y(), ir.y());
+ EXPECT_EQ(r3.width(), ir.width());
+ EXPECT_EQ(r3.height(), ir.height());
+ }
+}
+
+TEST(RectTest, Union) {
+ static const struct Test {
+ int x1; // rect 1
+ int y1;
+ int w1;
+ int h1;
+ int x2; // rect 2
+ int y2;
+ int w2;
+ int h2;
+ int x3; // rect 3: the union of rects 1 and 2
+ int y3;
+ int w3;
+ int h3;
+ } tests[] = {
+ { 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0 },
+ { 0, 0, 4, 4,
+ 0, 0, 4, 4,
+ 0, 0, 4, 4 },
+ { 0, 0, 4, 4,
+ 4, 4, 4, 4,
+ 0, 0, 8, 8 },
+ { 0, 0, 4, 4,
+ 0, 5, 4, 4,
+ 0, 0, 4, 9 },
+ { 0, 0, 2, 2,
+ 3, 3, 2, 2,
+ 0, 0, 5, 5 },
+ { 3, 3, 2, 2, // reverse r1 and r2 from previous test
+ 0, 0, 2, 2,
+ 0, 0, 5, 5 },
+ { 0, 0, 0, 0, // union with empty rect
+ 2, 2, 2, 2,
+ 2, 2, 2, 2 }
+ };
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
+ gfx::Rect r1(tests[i].x1, tests[i].y1, tests[i].w1, tests[i].h1);
+ gfx::Rect r2(tests[i].x2, tests[i].y2, tests[i].w2, tests[i].h2);
+ gfx::Rect r3(tests[i].x3, tests[i].y3, tests[i].w3, tests[i].h3);
+ gfx::Rect u = r1.Union(r2);
+ EXPECT_EQ(r3.x(), u.x());
+ EXPECT_EQ(r3.y(), u.y());
+ EXPECT_EQ(r3.width(), u.width());
+ EXPECT_EQ(r3.height(), u.height());
+ }
+}
+
+TEST(RectTest, Equals) {
+ ASSERT_TRUE(gfx::Rect(0, 0, 0, 0).Equals(gfx::Rect(0, 0, 0, 0)));
+ ASSERT_TRUE(gfx::Rect(1, 2, 3, 4).Equals(gfx::Rect(1, 2, 3, 4)));
+ ASSERT_FALSE(gfx::Rect(0, 0, 0, 0).Equals(gfx::Rect(0, 0, 0, 1)));
+ ASSERT_FALSE(gfx::Rect(0, 0, 0, 0).Equals(gfx::Rect(0, 0, 1, 0)));
+ ASSERT_FALSE(gfx::Rect(0, 0, 0, 0).Equals(gfx::Rect(0, 1, 0, 0)));
+ ASSERT_FALSE(gfx::Rect(0, 0, 0, 0).Equals(gfx::Rect(1, 0, 0, 0)));
+}
+
+TEST(RectTest, AdjustToFit) {
+ static const struct Test {
+ int x1; // source
+ int y1;
+ int w1;
+ int h1;
+ int x2; // target
+ int y2;
+ int w2;
+ int h2;
+ int x3; // rect 3: results of invoking AdjustToFit
+ int y3;
+ int w3;
+ int h3;
+ } tests[] = {
+ { 0, 0, 2, 2,
+ 0, 0, 2, 2,
+ 0, 0, 2, 2 },
+ { 2, 2, 3, 3,
+ 0, 0, 4, 4,
+ 1, 1, 3, 3 },
+ { -1, -1, 5, 5,
+ 0, 0, 4, 4,
+ 0, 0, 4, 4 },
+ { 2, 2, 4, 4,
+ 0, 0, 3, 3,
+ 0, 0, 3, 3 },
+ { 2, 2, 1, 1,
+ 0, 0, 3, 3,
+ 2, 2, 1, 1 }
+ };
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
+ gfx::Rect r1(tests[i].x1, tests[i].y1, tests[i].w1, tests[i].h1);
+ gfx::Rect r2(tests[i].x2, tests[i].y2, tests[i].w2, tests[i].h2);
+ gfx::Rect r3(tests[i].x3, tests[i].y3, tests[i].w3, tests[i].h3);
+ gfx::Rect u(r1.AdjustToFit(r2));
+ EXPECT_EQ(r3.x(), u.x());
+ EXPECT_EQ(r3.y(), u.y());
+ EXPECT_EQ(r3.width(), u.width());
+ EXPECT_EQ(r3.height(), u.height());
+ }
+}
+
+TEST(RectTest, Subtract) {
+ // Matching
+ EXPECT_TRUE(
+ gfx::Rect(10, 10, 20, 20).Subtract(
+ gfx::Rect(10, 10, 20, 20)).Equals(
+ gfx::Rect(0, 0, 0, 0)));
+
+ // Contains
+ EXPECT_TRUE(
+ gfx::Rect(10, 10, 20, 20).Subtract(
+ gfx::Rect(5, 5, 30, 30)).Equals(
+ gfx::Rect(0, 0, 0, 0)));
+
+ // No intersection
+ EXPECT_TRUE(
+ gfx::Rect(10, 10, 20, 20).Subtract(
+ gfx::Rect(30, 30, 20, 20)).Equals(
+ gfx::Rect(10, 10, 20, 20)));
+
+ // Not a complete intersection in either direction
+ EXPECT_TRUE(
+ gfx::Rect(10, 10, 20, 20).Subtract(
+ gfx::Rect(15, 15, 20, 20)).Equals(
+ gfx::Rect(10, 10, 20, 20)));
+
+ // Complete intersection in the x-direction
+ EXPECT_TRUE(
+ gfx::Rect(10, 10, 20, 20).Subtract(
+ gfx::Rect(10, 15, 20, 20)).Equals(
+ gfx::Rect(10, 10, 20, 5)));
+
+ // Complete intersection in the x-direction
+ EXPECT_TRUE(
+ gfx::Rect(10, 10, 20, 20).Subtract(
+ gfx::Rect(5, 15, 30, 20)).Equals(
+ gfx::Rect(10, 10, 20, 5)));
+
+ // Complete intersection in the x-direction
+ EXPECT_TRUE(
+ gfx::Rect(10, 10, 20, 20).Subtract(
+ gfx::Rect(5, 5, 30, 20)).Equals(
+ gfx::Rect(10, 25, 20, 5)));
+
+ // Complete intersection in the y-direction
+ EXPECT_TRUE(
+ gfx::Rect(10, 10, 20, 20).Subtract(
+ gfx::Rect(10, 10, 10, 30)).Equals(
+ gfx::Rect(20, 10, 10, 20)));
+
+ // Complete intersection in the y-direction
+ EXPECT_TRUE(
+ gfx::Rect(10, 10, 20, 20).Subtract(
+ gfx::Rect(5, 5, 20, 30)).Equals(
+ gfx::Rect(25, 10, 5, 20)));
+}
+
+TEST(RectTest, IsEmpty) {
+ EXPECT_TRUE(gfx::Rect(0, 0, 0, 0).IsEmpty());
+ EXPECT_TRUE(gfx::Rect(0, 0, 0, 0).size().IsEmpty());
+ EXPECT_TRUE(gfx::Rect(0, 0, 10, 0).IsEmpty());
+ EXPECT_TRUE(gfx::Rect(0, 0, 10, 0).size().IsEmpty());
+ EXPECT_TRUE(gfx::Rect(0, 0, 0, 10).IsEmpty());
+ EXPECT_TRUE(gfx::Rect(0, 0, 0, 10).size().IsEmpty());
+ EXPECT_FALSE(gfx::Rect(0, 0, 10, 10).IsEmpty());
+ EXPECT_FALSE(gfx::Rect(0, 0, 10, 10).size().IsEmpty());
+}
+
+TEST(RectTest, SharesEdgeWith) {
+ gfx::Rect r(2, 3, 4, 5);
+
+ // Must be non-overlapping
+ EXPECT_FALSE(r.SharesEdgeWith(r));
+
+ gfx::Rect just_above(2, 1, 4, 2);
+ gfx::Rect just_below(2, 8, 4, 2);
+ gfx::Rect just_left(0, 3, 2, 5);
+ gfx::Rect just_right(6, 3, 2, 5);
+
+ EXPECT_TRUE(r.SharesEdgeWith(just_above));
+ EXPECT_TRUE(r.SharesEdgeWith(just_below));
+ EXPECT_TRUE(r.SharesEdgeWith(just_left));
+ EXPECT_TRUE(r.SharesEdgeWith(just_right));
+
+ // Wrong placement
+ gfx::Rect same_height_no_edge(0, 0, 1, 5);
+ gfx::Rect same_width_no_edge(0, 0, 4, 1);
+
+ EXPECT_FALSE(r.SharesEdgeWith(same_height_no_edge));
+ EXPECT_FALSE(r.SharesEdgeWith(same_width_no_edge));
+
+ gfx::Rect just_above_no_edge(2, 1, 5, 2); // too wide
+ gfx::Rect just_below_no_edge(2, 8, 3, 2); // too narrow
+ gfx::Rect just_left_no_edge(0, 3, 2, 6); // too tall
+ gfx::Rect just_right_no_edge(6, 3, 2, 4); // too short
+
+ EXPECT_FALSE(r.SharesEdgeWith(just_above_no_edge));
+ EXPECT_FALSE(r.SharesEdgeWith(just_below_no_edge));
+ EXPECT_FALSE(r.SharesEdgeWith(just_left_no_edge));
+ EXPECT_FALSE(r.SharesEdgeWith(just_right_no_edge));
+}
diff --git a/ui/gfx/run_all_unittests.cc b/ui/gfx/run_all_unittests.cc
new file mode 100644
index 0000000..834d7c8
--- /dev/null
+++ b/ui/gfx/run_all_unittests.cc
@@ -0,0 +1,9 @@
+// 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/test_suite.h"
+
+int main(int argc, char** argv) {
+ return GfxTestSuite(argc, argv).Run();
+}
diff --git a/ui/gfx/scoped_cg_context_state_mac.h b/ui/gfx/scoped_cg_context_state_mac.h
new file mode 100644
index 0000000..6380bf1
--- /dev/null
+++ b/ui/gfx/scoped_cg_context_state_mac.h
@@ -0,0 +1,30 @@
+// 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.
+
+#ifndef UI_GFX_SCOPED_CG_CONTEXT_STATE_MAC_H_
+#define UI_GFX_SCOPED_CG_CONTEXT_STATE_MAC_H_
+
+#import <QuartzCore/QuartzCore.h>
+
+namespace gfx {
+
+class ScopedCGContextSaveGState {
+ public:
+ explicit ScopedCGContextSaveGState(CGContextRef context) : context_(context) {
+ CGContextSaveGState(context_);
+ }
+
+ ~ScopedCGContextSaveGState() {
+ CGContextRestoreGState(context_);
+ }
+
+ private:
+ CGContextRef context_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedCGContextSaveGState);
+};
+
+} // namespace gfx
+
+#endif // UI_GFX_SCOPED_CG_CONTEXT_STATE_MAC_H_
diff --git a/ui/gfx/scoped_image.h b/ui/gfx/scoped_image.h
new file mode 100644
index 0000000..58cb900f
--- /dev/null
+++ b/ui/gfx/scoped_image.h
@@ -0,0 +1,147 @@
+// 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.
+
+#ifndef UI_GFX_SCOPED_IMAGE_H_
+#define UI_GFX_SCOPED_IMAGE_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "build/build_config.h"
+#include "gfx/native_widget_types.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+
+#if defined(OS_LINUX)
+#include <glib-object.h>
+#elif defined(OS_MACOSX)
+#include "base/mac/mac_util.h"
+#endif
+
+namespace gfx {
+
+namespace internal {
+
+// ScopedImage is class that encapsulates one of the three platform-specific
+// images used: SkBitmap, NSImage, and GdkPixbuf. This is the abstract interface
+// that all ScopedImages respond to. This wrapper expects to own the image it
+// holds, unless it is Release()ed or Free()ed.
+//
+// This class is abstract and callers should use the specialized versions below,
+// which are not in the internal namespace.
+template <class ImageType>
+class ScopedImage {
+ public:
+ virtual ~ScopedImage() {}
+
+ // Frees the actual image that this boxes.
+ virtual void Free() = 0;
+
+ // Returns the image that this boxes.
+ ImageType* Get() {
+ return image_;
+ }
+
+ // Frees the current image and sets a new one.
+ void Set(ImageType* new_image) {
+ Free();
+ image_ = new_image;
+ }
+
+ // Returns the image this boxes and relinquishes ownership.
+ ImageType* Release() {
+ ImageType* tmp = image_;
+ image_ = NULL;
+ return tmp;
+ }
+
+ protected:
+ explicit ScopedImage(ImageType* image) : image_(image) {}
+ ImageType* image_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ScopedImage);
+};
+
+} // namespace internal
+
+// Generic template.
+template <class ImageType = gfx::NativeImageType>
+class ScopedImage : public gfx::internal::ScopedImage<ImageType> {
+ public:
+ explicit ScopedImage(gfx::NativeImage image)
+ : gfx::internal::ScopedImage<ImageType>(image) {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ScopedImage<ImageType>);
+};
+
+// Specialization for SkBitmap on all platforms.
+template <>
+class ScopedImage<SkBitmap> : public gfx::internal::ScopedImage<SkBitmap> {
+ public:
+ explicit ScopedImage(SkBitmap* image)
+ : gfx::internal::ScopedImage<SkBitmap>(image) {}
+ virtual ~ScopedImage() {
+ Free();
+ }
+
+ virtual void Free() {
+ delete image_;
+ image_ = NULL;
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ScopedImage);
+};
+
+// Specialization for the NSImage type on Mac OS X.
+#if defined(OS_MACOSX)
+template <>
+class ScopedImage<NSImage> : public gfx::internal::ScopedImage<NSImage> {
+ public:
+ explicit ScopedImage(NSImage* image)
+ : gfx::internal::ScopedImage<NSImage>(image) {}
+ virtual ~ScopedImage() {
+ Free();
+ }
+
+ virtual void Free() {
+ base::mac::NSObjectRelease(image_);
+ image_ = NULL;
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ScopedImage);
+};
+#endif // defined(OS_MACOSX)
+
+// Specialization for the GdkPixbuf type on Linux.
+#if defined(OS_LINUX)
+template <>
+class ScopedImage<GdkPixbuf> : public gfx::internal::ScopedImage<GdkPixbuf> {
+ public:
+ explicit ScopedImage(GdkPixbuf* image)
+ : gfx::internal::ScopedImage<GdkPixbuf>(image) {}
+ virtual ~ScopedImage() {
+ Free();
+ }
+
+ virtual void Free() {
+ if (image_) {
+ g_object_unref(image_);
+ image_ = NULL;
+ }
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ScopedImage);
+};
+#endif // defined(OS_LINUX)
+
+// Typedef ScopedNativeImage to the default template argument. This allows for
+// easy exchange between gfx::NativeImage and a gfx::ScopedNativeImage.
+typedef ScopedImage<> ScopedNativeImage;
+
+} // namespace gfx
+
+#endif // UI_GFX_SCOPED_IMAGE_H_
diff --git a/ui/gfx/scoped_image_unittest.cc b/ui/gfx/scoped_image_unittest.cc
new file mode 100644
index 0000000..8e8b312
--- /dev/null
+++ b/ui/gfx/scoped_image_unittest.cc
@@ -0,0 +1,98 @@
+// 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 "base/scoped_ptr.h"
+#include "gfx/scoped_image.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+
+#if defined(OS_LINUX)
+#include "gfx/gtk_util.h"
+#elif defined(OS_MACOSX)
+#include "base/mac/mac_util.h"
+#include "skia/ext/skia_utils_mac.h"
+#endif
+
+namespace {
+
+class ScopedImageTest : public testing::Test {
+ public:
+ SkBitmap* CreateBitmap() {
+ SkBitmap* bitmap = new SkBitmap();
+ bitmap->setConfig(SkBitmap::kARGB_8888_Config, 25, 25);
+ bitmap->allocPixels();
+ bitmap->eraseRGB(255, 0, 0);
+ return bitmap;
+ }
+
+ gfx::NativeImage CreateNativeImage() {
+ scoped_ptr<SkBitmap> bitmap(CreateBitmap());
+#if defined(OS_MACOSX)
+ NSImage* image = gfx::SkBitmapToNSImage(*(bitmap.get()));
+ base::mac::NSObjectRetain(image);
+ return image;
+#elif defined(OS_LINUX) && !defined(TOOLKIT_VIEWS)
+ return gfx::GdkPixbufFromSkBitmap(bitmap.get());
+#else
+ return bitmap.release();
+#endif
+ }
+};
+
+TEST_F(ScopedImageTest, Initialize) {
+ gfx::ScopedImage<SkBitmap> image(CreateBitmap());
+ EXPECT_TRUE(image.Get());
+}
+
+TEST_F(ScopedImageTest, Free) {
+ gfx::ScopedImage<SkBitmap> image(CreateBitmap());
+ EXPECT_TRUE(image.Get());
+ image.Free();
+ EXPECT_FALSE(image.Get());
+}
+
+TEST_F(ScopedImageTest, Release) {
+ gfx::ScopedImage<SkBitmap> image(CreateBitmap());
+ EXPECT_TRUE(image.Get());
+ scoped_ptr<SkBitmap> bitmap(image.Release());
+ EXPECT_FALSE(image.Get());
+}
+
+TEST_F(ScopedImageTest, Set) {
+ gfx::ScopedImage<SkBitmap> image(CreateBitmap());
+ EXPECT_TRUE(image.Get());
+ SkBitmap* image2 = CreateBitmap();
+ image.Set(image2);
+ EXPECT_EQ(image2, image.Get());
+}
+
+TEST_F(ScopedImageTest, NativeInitialize) {
+ gfx::ScopedNativeImage image(CreateNativeImage());
+ EXPECT_TRUE(image.Get());
+}
+
+TEST_F(ScopedImageTest, NativeFree) {
+ gfx::ScopedNativeImage image(CreateNativeImage());
+ EXPECT_TRUE(image.Get());
+ image.Free();
+ EXPECT_FALSE(image.Get());
+}
+
+TEST_F(ScopedImageTest, NativeRelease) {
+ gfx::ScopedNativeImage image(CreateNativeImage());
+ EXPECT_TRUE(image.Get());
+ gfx::ScopedNativeImage image2(image.Release());
+ EXPECT_FALSE(image.Get());
+ EXPECT_TRUE(image2.Get());
+}
+
+TEST_F(ScopedImageTest, NativeSet) {
+ gfx::ScopedNativeImage image(CreateNativeImage());
+ EXPECT_TRUE(image.Get());
+ gfx::NativeImage image2 = CreateNativeImage();
+ image.Set(image2);
+ EXPECT_EQ(image2, image.Get());
+}
+
+} // namespace
diff --git a/ui/gfx/scrollbar_size.cc b/ui/gfx/scrollbar_size.cc
new file mode 100644
index 0000000..426b0ac
--- /dev/null
+++ b/ui/gfx/scrollbar_size.cc
@@ -0,0 +1,23 @@
+// 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/scrollbar_size.h"
+
+#include "base/compiler_specific.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
+namespace gfx {
+
+int scrollbar_size() {
+#if defined(OS_WIN)
+ return GetSystemMetrics(SM_CXVSCROLL);
+#else
+ return 15;
+#endif
+}
+
+} // namespace gfx
diff --git a/ui/gfx/scrollbar_size.h b/ui/gfx/scrollbar_size.h
new file mode 100644
index 0000000..771b9db
--- /dev/null
+++ b/ui/gfx/scrollbar_size.h
@@ -0,0 +1,18 @@
+// 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 UI_GFX_SCROLLBAR_SIZE_H_
+#define UI_GFX_SCROLLBAR_SIZE_H_
+#pragma once
+
+namespace gfx {
+
+// This should return the thickness, in pixels, of a scrollbar in web content.
+// This needs to match the values in WebCore's
+// ScrollbarThemeChromiumXXX.cpp::scrollbarThickness().
+int scrollbar_size();
+
+} // namespace gfx
+
+#endif // UI_GFX_SCROLLBAR_SIZE_H_
diff --git a/ui/gfx/size.cc b/ui/gfx/size.cc
new file mode 100644
index 0000000..6e5528e
--- /dev/null
+++ b/ui/gfx/size.cc
@@ -0,0 +1,70 @@
+// 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/size.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#elif defined(OS_MACOSX)
+#include <CoreGraphics/CGGeometry.h>
+#endif
+
+#include <ostream>
+
+#include "base/logging.h"
+
+namespace gfx {
+
+Size::Size(int width, int height) {
+ set_width(width);
+ set_height(height);
+}
+
+#if defined(OS_MACOSX)
+Size::Size(const CGSize& s) {
+ set_width(s.width);
+ set_height(s.height);
+}
+
+Size& Size::operator=(const CGSize& s) {
+ set_width(s.width);
+ set_height(s.height);
+ return *this;
+}
+#endif
+
+#if defined(OS_WIN)
+SIZE Size::ToSIZE() const {
+ SIZE s;
+ s.cx = width_;
+ s.cy = height_;
+ return s;
+}
+#elif defined(OS_MACOSX)
+CGSize Size::ToCGSize() const {
+ return CGSizeMake(width_, height_);
+}
+#endif
+
+void Size::set_width(int width) {
+ if (width < 0) {
+ NOTREACHED() << "negative width:" << width;
+ width = 0;
+ }
+ width_ = width;
+}
+
+void Size::set_height(int height) {
+ if (height < 0) {
+ NOTREACHED() << "negative height:" << height;
+ height = 0;
+ }
+ height_ = height;
+}
+
+std::ostream& operator<<(std::ostream& out, const gfx::Size& s) {
+ return out << s.width() << "x" << s.height();
+}
+
+} // namespace gfx
diff --git a/ui/gfx/size.h b/ui/gfx/size.h
new file mode 100644
index 0000000..55468eae
--- /dev/null
+++ b/ui/gfx/size.h
@@ -0,0 +1,82 @@
+// 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.
+
+#ifndef UI_GFX_SIZE_H_
+#define UI_GFX_SIZE_H_
+#pragma once
+
+#include "build/build_config.h"
+
+#include <iosfwd>
+
+#if defined(OS_WIN)
+typedef struct tagSIZE SIZE;
+#elif defined(OS_MACOSX)
+#include <ApplicationServices/ApplicationServices.h>
+#endif
+
+namespace gfx {
+
+// A size has width and height values.
+class Size {
+ public:
+ Size() : width_(0), height_(0) {}
+ Size(int width, int height);
+#if defined(OS_MACOSX)
+ explicit Size(const CGSize& s);
+#endif
+
+ ~Size() {}
+
+#if defined(OS_MACOSX)
+ Size& operator=(const CGSize& s);
+#endif
+
+ int width() const { return width_; }
+ int height() const { return height_; }
+
+ int GetArea() const { return width_ * height_; }
+
+ void SetSize(int width, int height) {
+ set_width(width);
+ set_height(height);
+ }
+
+ void Enlarge(int width, int height) {
+ set_width(width_ + width);
+ set_height(height_ + height);
+ }
+
+ void set_width(int width);
+ void set_height(int height);
+
+ bool operator==(const Size& s) const {
+ return width_ == s.width_ && height_ == s.height_;
+ }
+
+ bool operator!=(const Size& s) const {
+ return !(*this == s);
+ }
+
+ bool IsEmpty() const {
+ // Size doesn't allow negative dimensions, so testing for 0 is enough.
+ return (width_ == 0) || (height_ == 0);
+ }
+
+#if defined(OS_WIN)
+ SIZE ToSIZE() const;
+#elif defined(OS_MACOSX)
+ CGSize ToCGSize() const;
+#endif
+
+ private:
+ int width_;
+ int height_;
+};
+
+std::ostream& operator<<(std::ostream& out, const gfx::Size& s);
+
+} // namespace gfx
+
+#endif // UI_GFX_SIZE_H_
diff --git a/ui/gfx/skbitmap_operations.cc b/ui/gfx/skbitmap_operations.cc
new file mode 100644
index 0000000..6899553
--- /dev/null
+++ b/ui/gfx/skbitmap_operations.cc
@@ -0,0 +1,721 @@
+// 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/skbitmap_operations.h"
+
+#include <algorithm>
+#include <string.h>
+
+#include "base/logging.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkColorPriv.h"
+#include "third_party/skia/include/core/SkUnPreMultiply.h"
+
+// static
+SkBitmap SkBitmapOperations::CreateInvertedBitmap(const SkBitmap& image) {
+ DCHECK(image.config() == SkBitmap::kARGB_8888_Config);
+
+ SkAutoLockPixels lock_image(image);
+
+ SkBitmap inverted;
+ inverted.setConfig(SkBitmap::kARGB_8888_Config, image.width(), image.height(),
+ 0);
+ inverted.allocPixels();
+ inverted.eraseARGB(0, 0, 0, 0);
+
+ for (int y = 0; y < image.height(); ++y) {
+ uint32* image_row = image.getAddr32(0, y);
+ uint32* dst_row = inverted.getAddr32(0, y);
+
+ for (int x = 0; x < image.width(); ++x) {
+ uint32 image_pixel = image_row[x];
+ dst_row[x] = (image_pixel & 0xFF000000) |
+ (0x00FFFFFF - (image_pixel & 0x00FFFFFF));
+ }
+ }
+
+ return inverted;
+}
+
+// static
+SkBitmap SkBitmapOperations::CreateSuperimposedBitmap(const SkBitmap& first,
+ const SkBitmap& second) {
+ DCHECK(first.width() == second.width());
+ DCHECK(first.height() == second.height());
+ DCHECK(first.bytesPerPixel() == second.bytesPerPixel());
+ DCHECK(first.config() == SkBitmap::kARGB_8888_Config);
+
+ SkAutoLockPixels lock_first(first);
+ SkAutoLockPixels lock_second(second);
+
+ SkBitmap superimposed;
+ superimposed.setConfig(SkBitmap::kARGB_8888_Config,
+ first.width(), first.height());
+ superimposed.allocPixels();
+ superimposed.eraseARGB(0, 0, 0, 0);
+
+ SkCanvas canvas(superimposed);
+
+ SkRect rect;
+ rect.fLeft = 0;
+ rect.fTop = 0;
+ rect.fRight = SkIntToScalar(first.width());
+ rect.fBottom = SkIntToScalar(first.height());
+
+ canvas.drawBitmapRect(first, NULL, rect);
+ canvas.drawBitmapRect(second, NULL, rect);
+
+ return superimposed;
+}
+
+// static
+SkBitmap SkBitmapOperations::CreateBlendedBitmap(const SkBitmap& first,
+ const SkBitmap& second,
+ double alpha) {
+ DCHECK((alpha >= 0) && (alpha <= 1));
+ DCHECK(first.width() == second.width());
+ DCHECK(first.height() == second.height());
+ DCHECK(first.bytesPerPixel() == second.bytesPerPixel());
+ DCHECK(first.config() == SkBitmap::kARGB_8888_Config);
+
+ // Optimize for case where we won't need to blend anything.
+ static const double alpha_min = 1.0 / 255;
+ static const double alpha_max = 254.0 / 255;
+ if (alpha < alpha_min)
+ return first;
+ else if (alpha > alpha_max)
+ return second;
+
+ SkAutoLockPixels lock_first(first);
+ SkAutoLockPixels lock_second(second);
+
+ SkBitmap blended;
+ blended.setConfig(SkBitmap::kARGB_8888_Config, first.width(), first.height(),
+ 0);
+ blended.allocPixels();
+ blended.eraseARGB(0, 0, 0, 0);
+
+ double first_alpha = 1 - alpha;
+
+ for (int y = 0; y < first.height(); ++y) {
+ uint32* first_row = first.getAddr32(0, y);
+ uint32* second_row = second.getAddr32(0, y);
+ uint32* dst_row = blended.getAddr32(0, y);
+
+ for (int x = 0; x < first.width(); ++x) {
+ uint32 first_pixel = first_row[x];
+ uint32 second_pixel = second_row[x];
+
+ int a = static_cast<int>((SkColorGetA(first_pixel) * first_alpha) +
+ (SkColorGetA(second_pixel) * alpha));
+ int r = static_cast<int>((SkColorGetR(first_pixel) * first_alpha) +
+ (SkColorGetR(second_pixel) * alpha));
+ int g = static_cast<int>((SkColorGetG(first_pixel) * first_alpha) +
+ (SkColorGetG(second_pixel) * alpha));
+ int b = static_cast<int>((SkColorGetB(first_pixel) * first_alpha) +
+ (SkColorGetB(second_pixel) * alpha));
+
+ dst_row[x] = SkColorSetARGB(a, r, g, b);
+ }
+ }
+
+ return blended;
+}
+
+// static
+SkBitmap SkBitmapOperations::CreateMaskedBitmap(const SkBitmap& rgb,
+ const SkBitmap& alpha) {
+ DCHECK(rgb.width() == alpha.width());
+ DCHECK(rgb.height() == alpha.height());
+ DCHECK(rgb.bytesPerPixel() == alpha.bytesPerPixel());
+ DCHECK(rgb.config() == SkBitmap::kARGB_8888_Config);
+ DCHECK(alpha.config() == SkBitmap::kARGB_8888_Config);
+
+ SkBitmap masked;
+ masked.setConfig(SkBitmap::kARGB_8888_Config, rgb.width(), rgb.height(), 0);
+ masked.allocPixels();
+ masked.eraseARGB(0, 0, 0, 0);
+
+ SkAutoLockPixels lock_rgb(rgb);
+ SkAutoLockPixels lock_alpha(alpha);
+ SkAutoLockPixels lock_masked(masked);
+
+ for (int y = 0; y < masked.height(); ++y) {
+ uint32* rgb_row = rgb.getAddr32(0, y);
+ uint32* alpha_row = alpha.getAddr32(0, y);
+ uint32* dst_row = masked.getAddr32(0, y);
+
+ for (int x = 0; x < masked.width(); ++x) {
+ SkColor rgb_pixel = SkUnPreMultiply::PMColorToColor(rgb_row[x]);
+ int alpha = SkAlphaMul(SkColorGetA(rgb_pixel), SkColorGetA(alpha_row[x]));
+ dst_row[x] = SkColorSetARGB(alpha,
+ SkAlphaMul(SkColorGetR(rgb_pixel), alpha),
+ SkAlphaMul(SkColorGetG(rgb_pixel), alpha),
+ SkAlphaMul(SkColorGetB(rgb_pixel), alpha));
+ }
+ }
+
+ return masked;
+}
+
+// static
+SkBitmap SkBitmapOperations::CreateButtonBackground(SkColor color,
+ const SkBitmap& image,
+ const SkBitmap& mask) {
+ DCHECK(image.config() == SkBitmap::kARGB_8888_Config);
+ DCHECK(mask.config() == SkBitmap::kARGB_8888_Config);
+
+ SkBitmap background;
+ background.setConfig(
+ SkBitmap::kARGB_8888_Config, mask.width(), mask.height(), 0);
+ background.allocPixels();
+
+ double bg_a = SkColorGetA(color);
+ double bg_r = SkColorGetR(color);
+ double bg_g = SkColorGetG(color);
+ double bg_b = SkColorGetB(color);
+
+ SkAutoLockPixels lock_mask(mask);
+ SkAutoLockPixels lock_image(image);
+ SkAutoLockPixels lock_background(background);
+
+ for (int y = 0; y < mask.height(); ++y) {
+ uint32* dst_row = background.getAddr32(0, y);
+ uint32* image_row = image.getAddr32(0, y % image.height());
+ uint32* mask_row = mask.getAddr32(0, y);
+
+ for (int x = 0; x < mask.width(); ++x) {
+ uint32 image_pixel = image_row[x % image.width()];
+
+ double img_a = SkColorGetA(image_pixel);
+ double img_r = SkColorGetR(image_pixel);
+ double img_g = SkColorGetG(image_pixel);
+ double img_b = SkColorGetB(image_pixel);
+
+ double img_alpha = static_cast<double>(img_a) / 255.0;
+ double img_inv = 1 - img_alpha;
+
+ double mask_a = static_cast<double>(SkColorGetA(mask_row[x])) / 255.0;
+
+ dst_row[x] = SkColorSetARGB(
+ static_cast<int>(std::min(255.0, bg_a + img_a) * mask_a),
+ static_cast<int>(((bg_r * img_inv) + (img_r * img_alpha)) * mask_a),
+ static_cast<int>(((bg_g * img_inv) + (img_g * img_alpha)) * mask_a),
+ static_cast<int>(((bg_b * img_inv) + (img_b * img_alpha)) * mask_a));
+ }
+ }
+
+ return background;
+}
+
+namespace {
+namespace HSLShift {
+
+// TODO(viettrungluu): Some things have yet to be optimized at all.
+
+// Notes on and conventions used in the following code
+//
+// Conventions:
+// - R, G, B, A = obvious; as variables: |r|, |g|, |b|, |a| (see also below)
+// - H, S, L = obvious; as variables: |h|, |s|, |l| (see also below)
+// - variables derived from S, L shift parameters: |sdec| and |sinc| for S
+// increase and decrease factors, |ldec| and |linc| for L (see also below)
+//
+// To try to optimize HSL shifts, we do several things:
+// - Avoid unpremultiplying (then processing) then premultiplying. This means
+// that R, G, B values (and also L, but not H and S) should be treated as
+// having a range of 0..A (where A is alpha).
+// - Do things in integer/fixed-point. This avoids costly conversions between
+// floating-point and integer, though I should study the tradeoff more
+// carefully (presumably, at some point of processing complexity, converting
+// and processing using simpler floating-point code will begin to win in
+// performance). Also to be studied is the speed/type of floating point
+// conversions; see, e.g., <http://www.stereopsis.com/sree/fpu2006.html>.
+//
+// Conventions for fixed-point arithmetic
+// - Each function has a constant denominator (called |den|, which should be a
+// power of 2), appropriate for the computations done in that function.
+// - A value |x| is then typically represented by a numerator, named |x_num|,
+// so that its actual value is |x_num / den| (casting to floating-point
+// before division).
+// - To obtain |x_num| from |x|, simply multiply by |den|, i.e., |x_num = x *
+// den| (casting appropriately).
+// - When necessary, a value |x| may also be represented as a numerator over
+// the denominator squared (set |den2 = den * den|). In such a case, the
+// corresponding variable is called |x_num2| (so that its actual value is
+// |x_num^2 / den2|.
+// - The representation of the product of |x| and |y| is be called |x_y_num| if
+// |x * y == x_y_num / den|, and |xy_num2| if |x * y == x_y_num2 / den2|. In
+// the latter case, notice that one can calculate |x_y_num2 = x_num * y_num|.
+
+// Routine used to process a line; typically specialized for specific kinds of
+// HSL shifts (to optimize).
+typedef void (*LineProcessor)(color_utils::HSL,
+ const SkPMColor*,
+ SkPMColor*,
+ int width);
+
+enum OperationOnH { kOpHNone = 0, kOpHShift, kNumHOps };
+enum OperationOnS { kOpSNone = 0, kOpSDec, kOpSInc, kNumSOps };
+enum OperationOnL { kOpLNone = 0, kOpLDec, kOpLInc, kNumLOps };
+
+// Epsilon used to judge when shift values are close enough to various critical
+// values (typically 0.5, which yields a no-op for S and L shifts. 1/256 should
+// be small enough, but let's play it safe>
+const double epsilon = 0.0005;
+
+// Line processor: default/universal (i.e., old-school).
+void LineProcDefault(color_utils::HSL hsl_shift, const SkPMColor* in,
+ SkPMColor* out, int width) {
+ for (int x = 0; x < width; x++) {
+ out[x] = SkPreMultiplyColor(color_utils::HSLShift(
+ SkUnPreMultiply::PMColorToColor(in[x]), hsl_shift));
+ }
+}
+
+// Line processor: no-op (i.e., copy).
+void LineProcCopy(color_utils::HSL hsl_shift, const SkPMColor* in,
+ SkPMColor* out, int width) {
+ DCHECK(hsl_shift.h < 0);
+ DCHECK(hsl_shift.s < 0 || fabs(hsl_shift.s - 0.5) < HSLShift::epsilon);
+ DCHECK(hsl_shift.l < 0 || fabs(hsl_shift.l - 0.5) < HSLShift::epsilon);
+ memcpy(out, in, static_cast<size_t>(width) * sizeof(out[0]));
+}
+
+// Line processor: H no-op, S no-op, L decrease.
+void LineProcHnopSnopLdec(color_utils::HSL hsl_shift, const SkPMColor* in,
+ SkPMColor* out, int width) {
+ const uint32_t den = 65536;
+
+ DCHECK(hsl_shift.h < 0);
+ DCHECK(hsl_shift.s < 0 || fabs(hsl_shift.s - 0.5) < HSLShift::epsilon);
+ DCHECK(hsl_shift.l <= 0.5 - HSLShift::epsilon && hsl_shift.l >= 0);
+
+ uint32_t ldec_num = static_cast<uint32_t>(hsl_shift.l * 2 * den);
+ for (int x = 0; x < width; x++) {
+ uint32_t a = SkGetPackedA32(in[x]);
+ uint32_t r = SkGetPackedR32(in[x]);
+ uint32_t g = SkGetPackedG32(in[x]);
+ uint32_t b = SkGetPackedB32(in[x]);
+ r = r * ldec_num / den;
+ g = g * ldec_num / den;
+ b = b * ldec_num / den;
+ out[x] = SkPackARGB32(a, r, g, b);
+ }
+}
+
+// Line processor: H no-op, S no-op, L increase.
+void LineProcHnopSnopLinc(color_utils::HSL hsl_shift, const SkPMColor* in,
+ SkPMColor* out, int width) {
+ const uint32_t den = 65536;
+
+ DCHECK(hsl_shift.h < 0);
+ DCHECK(hsl_shift.s < 0 || fabs(hsl_shift.s - 0.5) < HSLShift::epsilon);
+ DCHECK(hsl_shift.l >= 0.5 + HSLShift::epsilon && hsl_shift.l <= 1);
+
+ uint32_t linc_num = static_cast<uint32_t>((hsl_shift.l - 0.5) * 2 * den);
+ for (int x = 0; x < width; x++) {
+ uint32_t a = SkGetPackedA32(in[x]);
+ uint32_t r = SkGetPackedR32(in[x]);
+ uint32_t g = SkGetPackedG32(in[x]);
+ uint32_t b = SkGetPackedB32(in[x]);
+ r += (a - r) * linc_num / den;
+ g += (a - g) * linc_num / den;
+ b += (a - b) * linc_num / den;
+ out[x] = SkPackARGB32(a, r, g, b);
+ }
+}
+
+// Saturation changes modifications in RGB
+//
+// (Note that as a further complication, the values we deal in are
+// premultiplied, so R/G/B values must be in the range 0..A. For mathematical
+// purposes, one may as well use r=R/A, g=G/A, b=B/A. Without loss of
+// generality, assume that R/G/B values are in the range 0..1.)
+//
+// Let Max = max(R,G,B), Min = min(R,G,B), and Med be the median value. Then L =
+// (Max+Min)/2. If L is to remain constant, Max+Min must also remain constant.
+//
+// For H to remain constant, first, the (numerical) order of R/G/B (from
+// smallest to largest) must remain the same. Second, all the ratios
+// (R-G)/(Max-Min), (R-B)/(Max-Min), (G-B)/(Max-Min) must remain constant (of
+// course, if Max = Min, then S = 0 and no saturation change is well-defined,
+// since H is not well-defined).
+//
+// Let C_max be a colour with value Max, C_min be one with value Min, and C_med
+// the remaining colour. Increasing saturation (to the maximum) is accomplished
+// by increasing the value of C_max while simultaneously decreasing C_min and
+// changing C_med so that the ratios are maintained; for the latter, it suffices
+// to keep (C_med-C_min)/(C_max-C_min) constant (and equal to
+// (Med-Min)/(Max-Min)).
+
+// Line processor: H no-op, S decrease, L no-op.
+void LineProcHnopSdecLnop(color_utils::HSL hsl_shift, const SkPMColor* in,
+ SkPMColor* out, int width) {
+ DCHECK(hsl_shift.h < 0);
+ DCHECK(hsl_shift.s >= 0 && hsl_shift.s <= 0.5 - HSLShift::epsilon);
+ DCHECK(hsl_shift.l < 0 || fabs(hsl_shift.l - 0.5) < HSLShift::epsilon);
+
+ const int32_t denom = 65536;
+ int32_t s_numer = static_cast<int32_t>(hsl_shift.s * 2 * denom);
+ for (int x = 0; x < width; x++) {
+ int32_t a = static_cast<int32_t>(SkGetPackedA32(in[x]));
+ int32_t r = static_cast<int32_t>(SkGetPackedR32(in[x]));
+ int32_t g = static_cast<int32_t>(SkGetPackedG32(in[x]));
+ int32_t b = static_cast<int32_t>(SkGetPackedB32(in[x]));
+
+ int32_t vmax, vmin;
+ if (r > g) { // This uses 3 compares rather than 4.
+ vmax = std::max(r, b);
+ vmin = std::min(g, b);
+ } else {
+ vmax = std::max(g, b);
+ vmin = std::min(r, b);
+ }
+
+ // Use denom * L to avoid rounding.
+ int32_t denom_l = (vmax + vmin) * (denom / 2);
+ int32_t s_numer_l = (vmax + vmin) * s_numer / 2;
+
+ r = (denom_l + r * s_numer - s_numer_l) / denom;
+ g = (denom_l + g * s_numer - s_numer_l) / denom;
+ b = (denom_l + b * s_numer - s_numer_l) / denom;
+ out[x] = SkPackARGB32(a, r, g, b);
+ }
+}
+
+// Line processor: H no-op, S decrease, L decrease.
+void LineProcHnopSdecLdec(color_utils::HSL hsl_shift, const SkPMColor* in,
+ SkPMColor* out, int width) {
+ DCHECK(hsl_shift.h < 0);
+ DCHECK(hsl_shift.s >= 0 && hsl_shift.s <= 0.5 - HSLShift::epsilon);
+ DCHECK(hsl_shift.l >= 0 && hsl_shift.l <= 0.5 - HSLShift::epsilon);
+
+ // Can't be too big since we need room for denom*denom and a bit for sign.
+ const int32_t denom = 1024;
+ int32_t l_numer = static_cast<int32_t>(hsl_shift.l * 2 * denom);
+ int32_t s_numer = static_cast<int32_t>(hsl_shift.s * 2 * denom);
+ for (int x = 0; x < width; x++) {
+ int32_t a = static_cast<int32_t>(SkGetPackedA32(in[x]));
+ int32_t r = static_cast<int32_t>(SkGetPackedR32(in[x]));
+ int32_t g = static_cast<int32_t>(SkGetPackedG32(in[x]));
+ int32_t b = static_cast<int32_t>(SkGetPackedB32(in[x]));
+
+ int32_t vmax, vmin;
+ if (r > g) { // This uses 3 compares rather than 4.
+ vmax = std::max(r, b);
+ vmin = std::min(g, b);
+ } else {
+ vmax = std::max(g, b);
+ vmin = std::min(r, b);
+ }
+
+ // Use denom * L to avoid rounding.
+ int32_t denom_l = (vmax + vmin) * (denom / 2);
+ int32_t s_numer_l = (vmax + vmin) * s_numer / 2;
+
+ r = (denom_l + r * s_numer - s_numer_l) * l_numer / (denom * denom);
+ g = (denom_l + g * s_numer - s_numer_l) * l_numer / (denom * denom);
+ b = (denom_l + b * s_numer - s_numer_l) * l_numer / (denom * denom);
+ out[x] = SkPackARGB32(a, r, g, b);
+ }
+}
+
+// Line processor: H no-op, S decrease, L increase.
+void LineProcHnopSdecLinc(color_utils::HSL hsl_shift, const SkPMColor* in,
+ SkPMColor* out, int width) {
+ DCHECK(hsl_shift.h < 0);
+ DCHECK(hsl_shift.s >= 0 && hsl_shift.s <= 0.5 - HSLShift::epsilon);
+ DCHECK(hsl_shift.l >= 0.5 + HSLShift::epsilon && hsl_shift.l <= 1);
+
+ // Can't be too big since we need room for denom*denom and a bit for sign.
+ const int32_t denom = 1024;
+ int32_t l_numer = static_cast<int32_t>((hsl_shift.l - 0.5) * 2 * denom);
+ int32_t s_numer = static_cast<int32_t>(hsl_shift.s * 2 * denom);
+ for (int x = 0; x < width; x++) {
+ int32_t a = static_cast<int32_t>(SkGetPackedA32(in[x]));
+ int32_t r = static_cast<int32_t>(SkGetPackedR32(in[x]));
+ int32_t g = static_cast<int32_t>(SkGetPackedG32(in[x]));
+ int32_t b = static_cast<int32_t>(SkGetPackedB32(in[x]));
+
+ int32_t vmax, vmin;
+ if (r > g) { // This uses 3 compares rather than 4.
+ vmax = std::max(r, b);
+ vmin = std::min(g, b);
+ } else {
+ vmax = std::max(g, b);
+ vmin = std::min(r, b);
+ }
+
+ // Use denom * L to avoid rounding.
+ int32_t denom_l = (vmax + vmin) * (denom / 2);
+ int32_t s_numer_l = (vmax + vmin) * s_numer / 2;
+
+ r = denom_l + r * s_numer - s_numer_l;
+ g = denom_l + g * s_numer - s_numer_l;
+ b = denom_l + b * s_numer - s_numer_l;
+
+ r = (r * denom + (a * denom - r) * l_numer) / (denom * denom);
+ g = (g * denom + (a * denom - g) * l_numer) / (denom * denom);
+ b = (b * denom + (a * denom - b) * l_numer) / (denom * denom);
+ out[x] = SkPackARGB32(a, r, g, b);
+ }
+}
+
+const LineProcessor kLineProcessors[kNumHOps][kNumSOps][kNumLOps] = {
+ { // H: kOpHNone
+ { // S: kOpSNone
+ LineProcCopy, // L: kOpLNone
+ LineProcHnopSnopLdec, // L: kOpLDec
+ LineProcHnopSnopLinc // L: kOpLInc
+ },
+ { // S: kOpSDec
+ LineProcHnopSdecLnop, // L: kOpLNone
+ LineProcHnopSdecLdec, // L: kOpLDec
+ LineProcHnopSdecLinc // L: kOpLInc
+ },
+ { // S: kOpSInc
+ LineProcDefault, // L: kOpLNone
+ LineProcDefault, // L: kOpLDec
+ LineProcDefault // L: kOpLInc
+ }
+ },
+ { // H: kOpHShift
+ { // S: kOpSNone
+ LineProcDefault, // L: kOpLNone
+ LineProcDefault, // L: kOpLDec
+ LineProcDefault // L: kOpLInc
+ },
+ { // S: kOpSDec
+ LineProcDefault, // L: kOpLNone
+ LineProcDefault, // L: kOpLDec
+ LineProcDefault // L: kOpLInc
+ },
+ { // S: kOpSInc
+ LineProcDefault, // L: kOpLNone
+ LineProcDefault, // L: kOpLDec
+ LineProcDefault // L: kOpLInc
+ }
+ }
+};
+
+} // namespace HSLShift
+} // namespace
+
+// static
+SkBitmap SkBitmapOperations::CreateHSLShiftedBitmap(
+ const SkBitmap& bitmap,
+ color_utils::HSL hsl_shift) {
+ // Default to NOPs.
+ HSLShift::OperationOnH H_op = HSLShift::kOpHNone;
+ HSLShift::OperationOnS S_op = HSLShift::kOpSNone;
+ HSLShift::OperationOnL L_op = HSLShift::kOpLNone;
+
+ if (hsl_shift.h >= 0 && hsl_shift.h <= 1)
+ H_op = HSLShift::kOpHShift;
+
+ // Saturation shift: 0 -> fully desaturate, 0.5 -> NOP, 1 -> fully saturate.
+ if (hsl_shift.s >= 0 && hsl_shift.s <= (0.5 - HSLShift::epsilon))
+ S_op = HSLShift::kOpSDec;
+ else if (hsl_shift.s >= (0.5 + HSLShift::epsilon))
+ S_op = HSLShift::kOpSInc;
+
+ // Lightness shift: 0 -> black, 0.5 -> NOP, 1 -> white.
+ if (hsl_shift.l >= 0 && hsl_shift.l <= (0.5 - HSLShift::epsilon))
+ L_op = HSLShift::kOpLDec;
+ else if (hsl_shift.l >= (0.5 + HSLShift::epsilon))
+ L_op = HSLShift::kOpLInc;
+
+ HSLShift::LineProcessor line_proc =
+ HSLShift::kLineProcessors[H_op][S_op][L_op];
+
+ DCHECK(bitmap.empty() == false);
+ DCHECK(bitmap.config() == SkBitmap::kARGB_8888_Config);
+
+ SkBitmap shifted;
+ shifted.setConfig(SkBitmap::kARGB_8888_Config, bitmap.width(),
+ bitmap.height(), 0);
+ shifted.allocPixels();
+ shifted.eraseARGB(0, 0, 0, 0);
+ shifted.setIsOpaque(false);
+
+ SkAutoLockPixels lock_bitmap(bitmap);
+ SkAutoLockPixels lock_shifted(shifted);
+
+ // Loop through the pixels of the original bitmap.
+ for (int y = 0; y < bitmap.height(); ++y) {
+ SkPMColor* pixels = bitmap.getAddr32(0, y);
+ SkPMColor* tinted_pixels = shifted.getAddr32(0, y);
+
+ (*line_proc)(hsl_shift, pixels, tinted_pixels, bitmap.width());
+ }
+
+ return shifted;
+}
+
+// static
+SkBitmap SkBitmapOperations::CreateTiledBitmap(const SkBitmap& source,
+ int src_x, int src_y,
+ int dst_w, int dst_h) {
+ DCHECK(source.getConfig() == SkBitmap::kARGB_8888_Config);
+
+ SkBitmap cropped;
+ cropped.setConfig(SkBitmap::kARGB_8888_Config, dst_w, dst_h, 0);
+ cropped.allocPixels();
+ cropped.eraseARGB(0, 0, 0, 0);
+
+ SkAutoLockPixels lock_source(source);
+ SkAutoLockPixels lock_cropped(cropped);
+
+ // Loop through the pixels of the original bitmap.
+ for (int y = 0; y < dst_h; ++y) {
+ int y_pix = (src_y + y) % source.height();
+ while (y_pix < 0)
+ y_pix += source.height();
+
+ uint32* source_row = source.getAddr32(0, y_pix);
+ uint32* dst_row = cropped.getAddr32(0, y);
+
+ for (int x = 0; x < dst_w; ++x) {
+ int x_pix = (src_x + x) % source.width();
+ while (x_pix < 0)
+ x_pix += source.width();
+
+ dst_row[x] = source_row[x_pix];
+ }
+ }
+
+ return cropped;
+}
+
+// static
+SkBitmap SkBitmapOperations::DownsampleByTwoUntilSize(const SkBitmap& bitmap,
+ int min_w, int min_h) {
+ if ((bitmap.width() <= min_w) || (bitmap.height() <= min_h) ||
+ (min_w < 0) || (min_h < 0))
+ return bitmap;
+
+ // Since bitmaps are refcounted, this copy will be fast.
+ SkBitmap current = bitmap;
+ while ((current.width() >= min_w * 2) && (current.height() >= min_h * 2) &&
+ (current.width() > 1) && (current.height() > 1))
+ current = DownsampleByTwo(current);
+ return current;
+}
+
+// static
+SkBitmap SkBitmapOperations::DownsampleByTwo(const SkBitmap& bitmap) {
+ // Handle the nop case.
+ if ((bitmap.width() <= 1) || (bitmap.height() <= 1))
+ return bitmap;
+
+ SkBitmap result;
+ result.setConfig(SkBitmap::kARGB_8888_Config,
+ (bitmap.width() + 1) / 2, (bitmap.height() + 1) / 2);
+ result.allocPixels();
+
+ SkAutoLockPixels lock(bitmap);
+ for (int dest_y = 0; dest_y < result.height(); ++dest_y) {
+ for (int dest_x = 0; dest_x < result.width(); ++dest_x) {
+ // This code is based on downsampleby2_proc32 in SkBitmap.cpp. It is very
+ // clever in that it does two channels at once: alpha and green ("ag")
+ // and red and blue ("rb"). Each channel gets averaged across 4 pixels
+ // to get the result.
+ int src_x = dest_x << 1;
+ int src_y = dest_y << 1;
+ const SkPMColor* cur_src = bitmap.getAddr32(src_x, src_y);
+ SkPMColor tmp, ag, rb;
+
+ // Top left pixel of the 2x2 block.
+ tmp = *cur_src;
+ ag = (tmp >> 8) & 0xFF00FF;
+ rb = tmp & 0xFF00FF;
+ if (src_x < (bitmap.width() - 1))
+ ++cur_src;
+
+ // Top right pixel of the 2x2 block.
+ tmp = *cur_src;
+ ag += (tmp >> 8) & 0xFF00FF;
+ rb += tmp & 0xFF00FF;
+ if (src_y < (bitmap.height() - 1))
+ cur_src = bitmap.getAddr32(src_x, src_y + 1);
+ else
+ cur_src = bitmap.getAddr32(src_x, src_y); // Move back to the first.
+
+ // Bottom left pixel of the 2x2 block.
+ tmp = *cur_src;
+ ag += (tmp >> 8) & 0xFF00FF;
+ rb += tmp & 0xFF00FF;
+ if (src_x < (bitmap.width() - 1))
+ ++cur_src;
+
+ // Bottom right pixel of the 2x2 block.
+ tmp = *cur_src;
+ ag += (tmp >> 8) & 0xFF00FF;
+ rb += tmp & 0xFF00FF;
+
+ // Put the channels back together, dividing each by 4 to get the average.
+ // |ag| has the alpha and green channels shifted right by 8 bits from
+ // there they should end up, so shifting left by 6 gives them in the
+ // correct position divided by 4.
+ *result.getAddr32(dest_x, dest_y) =
+ ((rb >> 2) & 0xFF00FF) | ((ag << 6) & 0xFF00FF00);
+ }
+ }
+
+ return result;
+}
+
+// static
+SkBitmap SkBitmapOperations::UnPreMultiply(const SkBitmap& bitmap) {
+ if (bitmap.isNull())
+ return bitmap;
+ if (bitmap.isOpaque())
+ return bitmap;
+
+ SkBitmap opaque_bitmap;
+ opaque_bitmap.setConfig(bitmap.config(), bitmap.width(), bitmap.height());
+ opaque_bitmap.allocPixels();
+
+ {
+ SkAutoLockPixels bitmap_lock(bitmap);
+ SkAutoLockPixels opaque_bitmap_lock(opaque_bitmap);
+ for (int y = 0; y < opaque_bitmap.height(); y++) {
+ for (int x = 0; x < opaque_bitmap.width(); x++) {
+ uint32 src_pixel = *bitmap.getAddr32(x, y);
+ uint32* dst_pixel = opaque_bitmap.getAddr32(x, y);
+ SkColor unmultiplied = SkUnPreMultiply::PMColorToColor(src_pixel);
+ *dst_pixel = unmultiplied;
+ }
+ }
+ }
+
+ opaque_bitmap.setIsOpaque(true);
+ return opaque_bitmap;
+}
+
+// static
+SkBitmap SkBitmapOperations::CreateTransposedBtmap(const SkBitmap& image) {
+ DCHECK(image.config() == SkBitmap::kARGB_8888_Config);
+
+ SkAutoLockPixels lock_image(image);
+
+ SkBitmap transposed;
+ transposed.setConfig(
+ SkBitmap::kARGB_8888_Config, image.height(), image.width(), 0);
+ transposed.allocPixels();
+ transposed.eraseARGB(0, 0, 0, 0);
+
+ for (int y = 0; y < image.height(); ++y) {
+ uint32* image_row = image.getAddr32(0, y);
+ for (int x = 0; x < image.width(); ++x) {
+ uint32* dst = transposed.getAddr32(y, x);
+ *dst = image_row[x];
+ }
+ }
+
+ return transposed;
+}
+
diff --git a/ui/gfx/skbitmap_operations.h b/ui/gfx/skbitmap_operations.h
new file mode 100644
index 0000000..12ae86a
--- /dev/null
+++ b/ui/gfx/skbitmap_operations.h
@@ -0,0 +1,102 @@
+// 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 UI_GFX_SKBITMAP_OPERATIONS_H_
+#define UI_GFX_SKBITMAP_OPERATIONS_H_
+#pragma once
+
+#include "base/gtest_prod_util.h"
+#include "gfx/color_utils.h"
+
+class SkBitmap;
+
+class SkBitmapOperations {
+ public:
+ // Create a bitmap that is an inverted image of the passed in image.
+ // Each color becomes its inverse in the color wheel. So (255, 15, 0) becomes
+ // (0, 240, 255). The alpha value is not inverted.
+ static SkBitmap CreateInvertedBitmap(const SkBitmap& image);
+
+ // Create a bitmap that is a superimposition of the second bitmap on top of
+ // the first. The provided bitmaps must use have the kARGB_8888_Config config
+ // and be of equal dimensions.
+ static SkBitmap CreateSuperimposedBitmap(const SkBitmap& first,
+ const SkBitmap& second);
+
+ // Create a bitmap that is a blend of two others. The alpha argument
+ // specifies the opacity of the second bitmap. The provided bitmaps must
+ // use have the kARGB_8888_Config config and be of equal dimensions.
+ static SkBitmap CreateBlendedBitmap(const SkBitmap& first,
+ const SkBitmap& second,
+ double alpha);
+
+ // Create a bitmap that is the original bitmap masked out by the mask defined
+ // in the alpha bitmap. The images must use the kARGB_8888_Config config and
+ // be of equal dimensions.
+ static SkBitmap CreateMaskedBitmap(const SkBitmap& first,
+ const SkBitmap& alpha);
+
+ // We create a button background image by compositing the color and image
+ // together, then applying the mask. This is a highly specialized composite
+ // operation that is the equivalent of drawing a background in |color|,
+ // tiling |image| over the top, and then masking the result out with |mask|.
+ // The images must use kARGB_8888_Config config.
+ static SkBitmap CreateButtonBackground(SkColor color,
+ const SkBitmap& image,
+ const SkBitmap& mask);
+
+ // Shift a bitmap's HSL values. The shift values are in the range of 0-1,
+ // with the option to specify -1 for 'no change'. The shift values are
+ // defined as:
+ // hsl_shift[0] (hue): The absolute hue value for the image - 0 and 1 map
+ // to 0 and 360 on the hue color wheel (red).
+ // hsl_shift[1] (saturation): A saturation shift for the image, with the
+ // following key values:
+ // 0 = remove all color.
+ // 0.5 = leave unchanged.
+ // 1 = fully saturate the image.
+ // hsl_shift[2] (lightness): A lightness shift for the image, with the
+ // following key values:
+ // 0 = remove all lightness (make all pixels black).
+ // 0.5 = leave unchanged.
+ // 1 = full lightness (make all pixels white).
+ static SkBitmap CreateHSLShiftedBitmap(const SkBitmap& bitmap,
+ color_utils::HSL hsl_shift);
+
+ // Create a bitmap that is cropped from another bitmap. This is special
+ // because it tiles the original bitmap, so your coordinates can extend
+ // outside the bounds of the original image.
+ static SkBitmap CreateTiledBitmap(const SkBitmap& bitmap,
+ int src_x, int src_y,
+ int dst_w, int dst_h);
+
+ // Iteratively downsamples by 2 until the bitmap is no smaller than the
+ // input size. The normal use of this is to downsample the bitmap "close" to
+ // the final size, and then use traditional resampling on the result.
+ // Because the bitmap will be closer to the final size, it will be faster,
+ // and linear interpolation will generally work well as a second step.
+ static SkBitmap DownsampleByTwoUntilSize(const SkBitmap& bitmap,
+ int min_w, int min_h);
+
+ // Makes a bitmap half has large in each direction by averaging groups of
+ // 4 pixels. This is one step in generating a mipmap.
+ static SkBitmap DownsampleByTwo(const SkBitmap& bitmap);
+
+ // Unpremultiplies all pixels in |bitmap|. You almost never want to call
+ // this, as |SkBitmap|s are always premultiplied by conversion. Call this
+ // only if you will pass the bitmap's data into a system function that
+ // doesn't expect premultiplied colors.
+ static SkBitmap UnPreMultiply(const SkBitmap& bitmap);
+
+ // Transpose the pixels in |bitmap| by swapping x and y.
+ static SkBitmap CreateTransposedBtmap(const SkBitmap& bitmap);
+
+ private:
+ SkBitmapOperations(); // Class for scoping only.
+
+ FRIEND_TEST_ALL_PREFIXES(SkBitmapOperationsTest, DownsampleByTwo);
+ FRIEND_TEST_ALL_PREFIXES(SkBitmapOperationsTest, DownsampleByTwoSmall);
+};
+
+#endif // UI_GFX_SKBITMAP_OPERATIONS_H_
diff --git a/ui/gfx/skbitmap_operations_unittest.cc b/ui/gfx/skbitmap_operations_unittest.cc
new file mode 100644
index 0000000..bcad287
--- /dev/null
+++ b/ui/gfx/skbitmap_operations_unittest.cc
@@ -0,0 +1,517 @@
+// 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/skbitmap_operations.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkColorPriv.h"
+#include "third_party/skia/include/core/SkUnPreMultiply.h"
+
+namespace {
+
+// Returns true if each channel of the given two colors are "close." This is
+// used for comparing colors where rounding errors may cause off-by-one.
+inline bool ColorsClose(uint32_t a, uint32_t b) {
+ return abs(static_cast<int>(SkColorGetB(a) - SkColorGetB(b))) <= 2 &&
+ abs(static_cast<int>(SkColorGetG(a) - SkColorGetG(b))) <= 2 &&
+ abs(static_cast<int>(SkColorGetR(a) - SkColorGetR(b))) <= 2 &&
+ abs(static_cast<int>(SkColorGetA(a) - SkColorGetA(b))) <= 2;
+}
+
+inline bool MultipliedColorsClose(uint32_t a, uint32_t b) {
+ return ColorsClose(SkUnPreMultiply::PMColorToColor(a),
+ SkUnPreMultiply::PMColorToColor(b));
+}
+
+bool BitmapsClose(const SkBitmap& a, const SkBitmap& b) {
+ SkAutoLockPixels a_lock(a);
+ SkAutoLockPixels b_lock(b);
+
+ for (int y = 0; y < a.height(); y++) {
+ for (int x = 0; x < a.width(); x++) {
+ SkColor a_pixel = *a.getAddr32(x, y);
+ SkColor b_pixel = *b.getAddr32(x, y);
+ if (!ColorsClose(a_pixel, b_pixel))
+ return false;
+ }
+ }
+ return true;
+}
+
+void FillDataToBitmap(int w, int h, SkBitmap* bmp) {
+ bmp->setConfig(SkBitmap::kARGB_8888_Config, w, h);
+ bmp->allocPixels();
+
+ unsigned char* src_data =
+ reinterpret_cast<unsigned char*>(bmp->getAddr32(0, 0));
+ for (int i = 0; i < w * h; i++) {
+ src_data[i * 4 + 0] = static_cast<unsigned char>(i % 255);
+ src_data[i * 4 + 1] = static_cast<unsigned char>(i % 255);
+ src_data[i * 4 + 2] = static_cast<unsigned char>(i % 255);
+ src_data[i * 4 + 3] = static_cast<unsigned char>(i % 255);
+ }
+}
+
+// The reference (i.e., old) implementation of |CreateHSLShiftedBitmap()|.
+SkBitmap ReferenceCreateHSLShiftedBitmap(
+ const SkBitmap& bitmap,
+ color_utils::HSL hsl_shift) {
+ SkBitmap shifted;
+ shifted.setConfig(SkBitmap::kARGB_8888_Config, bitmap.width(),
+ bitmap.height(), 0);
+ shifted.allocPixels();
+ shifted.eraseARGB(0, 0, 0, 0);
+ shifted.setIsOpaque(false);
+
+ SkAutoLockPixels lock_bitmap(bitmap);
+ SkAutoLockPixels lock_shifted(shifted);
+
+ // Loop through the pixels of the original bitmap.
+ for (int y = 0; y < bitmap.height(); ++y) {
+ SkPMColor* pixels = bitmap.getAddr32(0, y);
+ SkPMColor* tinted_pixels = shifted.getAddr32(0, y);
+
+ for (int x = 0; x < bitmap.width(); ++x) {
+ tinted_pixels[x] = SkPreMultiplyColor(color_utils::HSLShift(
+ SkUnPreMultiply::PMColorToColor(pixels[x]), hsl_shift));
+ }
+ }
+
+ return shifted;
+}
+
+} // namespace
+
+// Invert bitmap and verify the each pixel is inverted and the alpha value is
+// not changed.
+TEST(SkBitmapOperationsTest, CreateInvertedBitmap) {
+ int src_w = 16, src_h = 16;
+ SkBitmap src;
+ src.setConfig(SkBitmap::kARGB_8888_Config, src_w, src_h);
+ src.allocPixels();
+
+ for (int y = 0; y < src_h; y++) {
+ for (int x = 0; x < src_w; x++) {
+ int i = y * src_w + x;
+ *src.getAddr32(x, y) =
+ SkColorSetARGB((255 - i) % 255, i % 255, i * 4 % 255, 0);
+ }
+ }
+
+ SkBitmap inverted = SkBitmapOperations::CreateInvertedBitmap(src);
+ SkAutoLockPixels src_lock(src);
+ SkAutoLockPixels inverted_lock(inverted);
+
+ for (int y = 0; y < src_h; y++) {
+ for (int x = 0; x < src_w; x++) {
+ int i = y * src_w + x;
+ EXPECT_EQ(static_cast<unsigned int>((255 - i) % 255),
+ SkColorGetA(*inverted.getAddr32(x, y)));
+ EXPECT_EQ(static_cast<unsigned int>(255 - (i % 255)),
+ SkColorGetR(*inverted.getAddr32(x, y)));
+ EXPECT_EQ(static_cast<unsigned int>(255 - (i * 4 % 255)),
+ SkColorGetG(*inverted.getAddr32(x, y)));
+ EXPECT_EQ(static_cast<unsigned int>(255),
+ SkColorGetB(*inverted.getAddr32(x, y)));
+ }
+ }
+}
+
+// Blend two bitmaps together at 50% alpha and verify that the result
+// is the middle-blend of the two.
+TEST(SkBitmapOperationsTest, CreateBlendedBitmap) {
+ int src_w = 16, src_h = 16;
+ SkBitmap src_a;
+ src_a.setConfig(SkBitmap::kARGB_8888_Config, src_w, src_h);
+ src_a.allocPixels();
+
+ SkBitmap src_b;
+ src_b.setConfig(SkBitmap::kARGB_8888_Config, src_w, src_h);
+ src_b.allocPixels();
+
+ for (int y = 0, i = 0; y < src_h; y++) {
+ for (int x = 0; x < src_w; x++) {
+ *src_a.getAddr32(x, y) = SkColorSetARGB(255, 0, i * 2 % 255, i % 255);
+ *src_b.getAddr32(x, y) =
+ SkColorSetARGB((255 - i) % 255, i % 255, i * 4 % 255, 0);
+ i++;
+ }
+ }
+
+ // Shift to red.
+ SkBitmap blended = SkBitmapOperations::CreateBlendedBitmap(
+ src_a, src_b, 0.5);
+ SkAutoLockPixels srca_lock(src_a);
+ SkAutoLockPixels srcb_lock(src_b);
+ SkAutoLockPixels blended_lock(blended);
+
+ for (int y = 0; y < src_h; y++) {
+ for (int x = 0; x < src_w; x++) {
+ int i = y * src_w + x;
+ EXPECT_EQ(static_cast<unsigned int>((255 + ((255 - i) % 255)) / 2),
+ SkColorGetA(*blended.getAddr32(x, y)));
+ EXPECT_EQ(static_cast<unsigned int>(i % 255 / 2),
+ SkColorGetR(*blended.getAddr32(x, y)));
+ EXPECT_EQ((static_cast<unsigned int>((i * 2) % 255 + (i * 4) % 255) / 2),
+ SkColorGetG(*blended.getAddr32(x, y)));
+ EXPECT_EQ(static_cast<unsigned int>(i % 255 / 2),
+ SkColorGetB(*blended.getAddr32(x, y)));
+ }
+ }
+}
+
+// Test our masking functions.
+TEST(SkBitmapOperationsTest, CreateMaskedBitmap) {
+ int src_w = 16, src_h = 16;
+
+ SkBitmap src;
+ FillDataToBitmap(src_w, src_h, &src);
+
+ // Generate alpha mask
+ SkBitmap alpha;
+ alpha.setConfig(SkBitmap::kARGB_8888_Config, src_w, src_h);
+ alpha.allocPixels();
+ for (int y = 0, i = 0; y < src_h; y++) {
+ for (int x = 0; x < src_w; x++) {
+ *alpha.getAddr32(x, y) = SkColorSetARGB((i + 128) % 255,
+ (i + 128) % 255,
+ (i + 64) % 255,
+ (i + 0) % 255);
+ i++;
+ }
+ }
+
+ SkBitmap masked = SkBitmapOperations::CreateMaskedBitmap(src, alpha);
+
+ SkAutoLockPixels src_lock(src);
+ SkAutoLockPixels alpha_lock(alpha);
+ SkAutoLockPixels masked_lock(masked);
+ for (int y = 0; y < src_h; y++) {
+ for (int x = 0; x < src_w; x++) {
+ // Test that the alpha is equal.
+ SkColor src_pixel = SkUnPreMultiply::PMColorToColor(*src.getAddr32(x, y));
+ SkColor alpha_pixel =
+ SkUnPreMultiply::PMColorToColor(*alpha.getAddr32(x, y));
+ SkColor masked_pixel = *masked.getAddr32(x, y);
+
+ int alpha_value = SkAlphaMul(SkColorGetA(src_pixel),
+ SkColorGetA(alpha_pixel));
+ SkColor expected_pixel = SkColorSetARGB(
+ alpha_value,
+ SkAlphaMul(SkColorGetR(src_pixel), alpha_value),
+ SkAlphaMul(SkColorGetG(src_pixel), alpha_value),
+ SkAlphaMul(SkColorGetB(src_pixel), alpha_value));
+
+ EXPECT_TRUE(ColorsClose(expected_pixel, masked_pixel));
+ }
+ }
+}
+
+// Make sure that when shifting a bitmap without any shift parameters,
+// the end result is close enough to the original (rounding errors
+// notwithstanding).
+TEST(SkBitmapOperationsTest, CreateHSLShiftedBitmapToSame) {
+ int src_w = 16, src_h = 16;
+ SkBitmap src;
+ src.setConfig(SkBitmap::kARGB_8888_Config, src_w, src_h);
+ src.allocPixels();
+
+ for (int y = 0, i = 0; y < src_h; y++) {
+ for (int x = 0; x < src_w; x++) {
+ *src.getAddr32(x, y) = SkPreMultiplyColor(SkColorSetARGB((i + 128) % 255,
+ (i + 128) % 255, (i + 64) % 255, (i + 0) % 255));
+ i++;
+ }
+ }
+
+ color_utils::HSL hsl = { -1, -1, -1 };
+ SkBitmap shifted = ReferenceCreateHSLShiftedBitmap(src, hsl);
+
+ SkAutoLockPixels src_lock(src);
+ SkAutoLockPixels shifted_lock(shifted);
+
+ for (int y = 0; y < src_h; y++) {
+ for (int x = 0; x < src_w; x++) {
+ SkColor src_pixel = *src.getAddr32(x, y);
+ SkColor shifted_pixel = *shifted.getAddr32(x, y);
+ EXPECT_TRUE(MultipliedColorsClose(src_pixel, shifted_pixel)) <<
+ "source: (a,r,g,b) = (" << SkColorGetA(src_pixel) << "," <<
+ SkColorGetR(src_pixel) << "," <<
+ SkColorGetG(src_pixel) << "," <<
+ SkColorGetB(src_pixel) << "); " <<
+ "shifted: (a,r,g,b) = (" << SkColorGetA(shifted_pixel) << "," <<
+ SkColorGetR(shifted_pixel) << "," <<
+ SkColorGetG(shifted_pixel) << "," <<
+ SkColorGetB(shifted_pixel) << ")";
+ }
+ }
+}
+
+// Shift a blue bitmap to red.
+TEST(SkBitmapOperationsTest, CreateHSLShiftedBitmapHueOnly) {
+ int src_w = 16, src_h = 16;
+ SkBitmap src;
+ src.setConfig(SkBitmap::kARGB_8888_Config, src_w, src_h);
+ src.allocPixels();
+
+ for (int y = 0, i = 0; y < src_h; y++) {
+ for (int x = 0; x < src_w; x++) {
+ *src.getAddr32(x, y) = SkColorSetARGB(255, 0, 0, i % 255);
+ i++;
+ }
+ }
+
+ // Shift to red.
+ color_utils::HSL hsl = { 0, -1, -1 };
+
+ SkBitmap shifted = SkBitmapOperations::CreateHSLShiftedBitmap(src, hsl);
+
+ SkAutoLockPixels src_lock(src);
+ SkAutoLockPixels shifted_lock(shifted);
+
+ for (int y = 0, i = 0; y < src_h; y++) {
+ for (int x = 0; x < src_w; x++) {
+ EXPECT_TRUE(ColorsClose(*shifted.getAddr32(x, y),
+ SkColorSetARGB(255, i % 255, 0, 0)));
+ i++;
+ }
+ }
+}
+
+// Validate HSL shift.
+TEST(SkBitmapOperationsTest, ValidateHSLShift) {
+ // Note: 255/51 = 5 (exactly) => 6 including 0!
+ const int inc = 51;
+ const int dim = 255 / inc + 1;
+ SkBitmap src;
+ src.setConfig(SkBitmap::kARGB_8888_Config, dim*dim, dim*dim);
+ src.allocPixels();
+
+ for (int a = 0, y = 0; a <= 255; a += inc) {
+ for (int r = 0; r <= 255; r += inc, y++) {
+ for (int g = 0, x = 0; g <= 255; g += inc) {
+ for (int b = 0; b <= 255; b+= inc, x++) {
+ *src.getAddr32(x, y) =
+ SkPreMultiplyColor(SkColorSetARGB(a, r, g, b));
+ }
+ }
+ }
+ }
+
+ // Shhhh. The spec says I should set things to -1 for "no change", but
+ // actually -0.1 will do. Don't tell anyone I did this.
+ for (double h = -0.1; h <= 1.0001; h += 0.1) {
+ for (double s = -0.1; s <= 1.0001; s += 0.1) {
+ for (double l = -0.1; l <= 1.0001; l += 0.1) {
+ color_utils::HSL hsl = { h, s, l };
+ SkBitmap ref_shifted = ReferenceCreateHSLShiftedBitmap(src, hsl);
+ SkBitmap shifted = SkBitmapOperations::CreateHSLShiftedBitmap(src, hsl);
+ EXPECT_TRUE(BitmapsClose(ref_shifted, shifted))
+ << "h = " << h << ", s = " << s << ", l = " << l;
+ }
+ }
+ }
+}
+
+// Test our cropping.
+TEST(SkBitmapOperationsTest, CreateCroppedBitmap) {
+ int src_w = 16, src_h = 16;
+ SkBitmap src;
+ FillDataToBitmap(src_w, src_h, &src);
+
+ SkBitmap cropped = SkBitmapOperations::CreateTiledBitmap(src, 4, 4,
+ 8, 8);
+ ASSERT_EQ(8, cropped.width());
+ ASSERT_EQ(8, cropped.height());
+
+ SkAutoLockPixels src_lock(src);
+ SkAutoLockPixels cropped_lock(cropped);
+ for (int y = 4; y < 12; y++) {
+ for (int x = 4; x < 12; x++) {
+ EXPECT_EQ(*src.getAddr32(x, y),
+ *cropped.getAddr32(x - 4, y - 4));
+ }
+ }
+}
+
+// Test whether our cropping correctly wraps across image boundaries.
+TEST(SkBitmapOperationsTest, CreateCroppedBitmapWrapping) {
+ int src_w = 16, src_h = 16;
+ SkBitmap src;
+ FillDataToBitmap(src_w, src_h, &src);
+
+ SkBitmap cropped = SkBitmapOperations::CreateTiledBitmap(
+ src, src_w / 2, src_h / 2, src_w, src_h);
+ ASSERT_EQ(src_w, cropped.width());
+ ASSERT_EQ(src_h, cropped.height());
+
+ SkAutoLockPixels src_lock(src);
+ SkAutoLockPixels cropped_lock(cropped);
+ for (int y = 0; y < src_h; y++) {
+ for (int x = 0; x < src_w; x++) {
+ EXPECT_EQ(*src.getAddr32(x, y),
+ *cropped.getAddr32((x + src_w / 2) % src_w,
+ (y + src_h / 2) % src_h));
+ }
+ }
+}
+
+TEST(SkBitmapOperationsTest, DownsampleByTwo) {
+ // Use an odd-sized bitmap to make sure the edge cases where there isn't a
+ // 2x2 block of pixels is handled correctly.
+ // Here's the ARGB example
+ //
+ // 50% transparent green opaque 50% blue white
+ // 80008000 FF000080 FFFFFFFF
+ //
+ // 50% transparent red opaque 50% gray black
+ // 80800000 80808080 FF000000
+ //
+ // black white 50% gray
+ // FF000000 FFFFFFFF FF808080
+ //
+ // The result of this computation should be:
+ // A0404040 FF808080
+ // FF808080 FF808080
+ SkBitmap input;
+ input.setConfig(SkBitmap::kARGB_8888_Config, 3, 3);
+ input.allocPixels();
+
+ // The color order may be different, but we don't care (the channels are
+ // trated the same).
+ *input.getAddr32(0, 0) = 0x80008000;
+ *input.getAddr32(1, 0) = 0xFF000080;
+ *input.getAddr32(2, 0) = 0xFFFFFFFF;
+ *input.getAddr32(0, 1) = 0x80800000;
+ *input.getAddr32(1, 1) = 0x80808080;
+ *input.getAddr32(2, 1) = 0xFF000000;
+ *input.getAddr32(0, 2) = 0xFF000000;
+ *input.getAddr32(1, 2) = 0xFFFFFFFF;
+ *input.getAddr32(2, 2) = 0xFF808080;
+
+ SkBitmap result = SkBitmapOperations::DownsampleByTwo(input);
+ EXPECT_EQ(2, result.width());
+ EXPECT_EQ(2, result.height());
+
+ // Some of the values are off-by-one due to rounding.
+ SkAutoLockPixels lock(result);
+ EXPECT_EQ(0x9f404040, *result.getAddr32(0, 0));
+ EXPECT_EQ(0xFF7f7f7f, *result.getAddr32(1, 0));
+ EXPECT_EQ(0xFF7f7f7f, *result.getAddr32(0, 1));
+ EXPECT_EQ(0xFF808080, *result.getAddr32(1, 1));
+}
+
+// Test edge cases for DownsampleByTwo.
+TEST(SkBitmapOperationsTest, DownsampleByTwoSmall) {
+ SkPMColor reference = 0xFF4080FF;
+
+ // Test a 1x1 bitmap.
+ SkBitmap one_by_one;
+ one_by_one.setConfig(SkBitmap::kARGB_8888_Config, 1, 1);
+ one_by_one.allocPixels();
+ *one_by_one.getAddr32(0, 0) = reference;
+ SkBitmap result = SkBitmapOperations::DownsampleByTwo(one_by_one);
+ SkAutoLockPixels lock1(result);
+ EXPECT_EQ(1, result.width());
+ EXPECT_EQ(1, result.height());
+ EXPECT_EQ(reference, *result.getAddr32(0, 0));
+
+ // Test an n by 1 bitmap.
+ SkBitmap one_by_n;
+ one_by_n.setConfig(SkBitmap::kARGB_8888_Config, 300, 1);
+ one_by_n.allocPixels();
+ result = SkBitmapOperations::DownsampleByTwo(one_by_n);
+ SkAutoLockPixels lock2(result);
+ EXPECT_EQ(300, result.width());
+ EXPECT_EQ(1, result.height());
+
+ // Test a 1 by n bitmap.
+ SkBitmap n_by_one;
+ n_by_one.setConfig(SkBitmap::kARGB_8888_Config, 1, 300);
+ n_by_one.allocPixels();
+ result = SkBitmapOperations::DownsampleByTwo(n_by_one);
+ SkAutoLockPixels lock3(result);
+ EXPECT_EQ(1, result.width());
+ EXPECT_EQ(300, result.height());
+
+ // Test an empty bitmap
+ SkBitmap empty;
+ result = SkBitmapOperations::DownsampleByTwo(empty);
+ EXPECT_TRUE(result.isNull());
+ EXPECT_EQ(0, result.width());
+ EXPECT_EQ(0, result.height());
+}
+
+// Here we assume DownsampleByTwo works correctly (it's tested above) and
+// just make sure that the wrapper function does the right thing.
+TEST(SkBitmapOperationsTest, DownsampleByTwoUntilSize) {
+ // First make sure a "too small" bitmap doesn't get modified at all.
+ SkBitmap too_small;
+ too_small.setConfig(SkBitmap::kARGB_8888_Config, 10, 10);
+ too_small.allocPixels();
+ SkBitmap result = SkBitmapOperations::DownsampleByTwoUntilSize(
+ too_small, 16, 16);
+ EXPECT_EQ(10, result.width());
+ EXPECT_EQ(10, result.height());
+
+ // Now make sure giving it a 0x0 target returns something reasonable.
+ result = SkBitmapOperations::DownsampleByTwoUntilSize(too_small, 0, 0);
+ EXPECT_EQ(1, result.width());
+ EXPECT_EQ(1, result.height());
+
+ // Test multiple steps of downsampling.
+ SkBitmap large;
+ large.setConfig(SkBitmap::kARGB_8888_Config, 100, 43);
+ large.allocPixels();
+ result = SkBitmapOperations::DownsampleByTwoUntilSize(large, 6, 6);
+
+ // The result should be divided in half 100x43 -> 50x22 -> 25x11
+ EXPECT_EQ(25, result.width());
+ EXPECT_EQ(11, result.height());
+}
+
+TEST(SkBitmapOperationsTest, UnPreMultiply) {
+ SkBitmap input;
+ input.setConfig(SkBitmap::kARGB_8888_Config, 2, 2);
+ input.allocPixels();
+
+ *input.getAddr32(0, 0) = 0x80000000;
+ *input.getAddr32(1, 0) = 0x80808080;
+ *input.getAddr32(0, 1) = 0xFF00CC88;
+ *input.getAddr32(1, 1) = 0x0000CC88;
+
+ SkBitmap result = SkBitmapOperations::UnPreMultiply(input);
+ EXPECT_EQ(2, result.width());
+ EXPECT_EQ(2, result.height());
+
+ SkAutoLockPixels lock(result);
+ EXPECT_EQ(0x80000000, *result.getAddr32(0, 0));
+ EXPECT_EQ(0x80FFFFFF, *result.getAddr32(1, 0));
+ EXPECT_EQ(0xFF00CC88, *result.getAddr32(0, 1));
+ EXPECT_EQ(0x00000000u, *result.getAddr32(1, 1)); // "Division by zero".
+}
+
+TEST(SkBitmapOperationsTest, CreateTransposedBtmap) {
+ SkBitmap input;
+ input.setConfig(SkBitmap::kARGB_8888_Config, 2, 3);
+ input.allocPixels();
+
+ for (int x = 0; x < input.width(); ++x) {
+ for (int y = 0; y < input.height(); ++y) {
+ *input.getAddr32(x, y) = x * input.width() + y;
+ }
+ }
+
+ SkBitmap result = SkBitmapOperations::CreateTransposedBtmap(input);
+ EXPECT_EQ(3, result.width());
+ EXPECT_EQ(2, result.height());
+
+ SkAutoLockPixels lock(result);
+ for (int x = 0; x < input.width(); ++x) {
+ for (int y = 0; y < input.height(); ++y) {
+ EXPECT_EQ(*input.getAddr32(x, y), *result.getAddr32(y, x));
+ }
+ }
+}
diff --git a/ui/gfx/skia_util.cc b/ui/gfx/skia_util.cc
new file mode 100644
index 0000000..865f8fda
--- /dev/null
+++ b/ui/gfx/skia_util.cc
@@ -0,0 +1,61 @@
+// 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/skia_util.h"
+
+#include "gfx/rect.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkColorPriv.h"
+#include "third_party/skia/include/core/SkShader.h"
+#include "third_party/skia/include/effects/SkGradientShader.h"
+
+namespace gfx {
+
+SkRect RectToSkRect(const gfx::Rect& rect) {
+ SkRect r;
+ r.set(SkIntToScalar(rect.x()), SkIntToScalar(rect.y()),
+ SkIntToScalar(rect.right()), SkIntToScalar(rect.bottom()));
+ return r;
+}
+
+gfx::Rect SkRectToRect(const SkRect& rect) {
+ return gfx::Rect(SkScalarToFixed(rect.fLeft),
+ SkScalarToFixed(rect.fTop),
+ SkScalarToFixed(rect.width()),
+ SkScalarToFixed(rect.height()));
+}
+
+SkShader* CreateGradientShader(int start_point,
+ int end_point,
+ SkColor start_color,
+ SkColor end_color) {
+ SkColor grad_colors[2] = { start_color, end_color};
+ SkPoint grad_points[2];
+ grad_points[0].set(SkIntToScalar(0), SkIntToScalar(start_point));
+ grad_points[1].set(SkIntToScalar(0), SkIntToScalar(end_point));
+
+ return SkGradientShader::CreateLinear(
+ grad_points, grad_colors, NULL, 2, SkShader::kRepeat_TileMode);
+}
+
+bool BitmapsAreEqual(const SkBitmap& bitmap1, const SkBitmap& bitmap2) {
+ void* addr1 = NULL;
+ void* addr2 = NULL;
+ size_t size1 = 0;
+ size_t size2 = 0;
+
+ bitmap1.lockPixels();
+ addr1 = bitmap1.getAddr32(0, 0);
+ size1 = bitmap1.getSize();
+ bitmap1.unlockPixels();
+
+ bitmap2.lockPixels();
+ addr2 = bitmap2.getAddr32(0, 0);
+ size2 = bitmap2.getSize();
+ bitmap2.unlockPixels();
+
+ return (size1 == size2) && (0 == memcmp(addr1, addr2, bitmap1.getSize()));
+}
+
+} // namespace gfx
diff --git a/ui/gfx/skia_util.h b/ui/gfx/skia_util.h
new file mode 100644
index 0000000..619ff37
--- /dev/null
+++ b/ui/gfx/skia_util.h
@@ -0,0 +1,39 @@
+// 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.
+
+#ifndef UI_GFX_SKIA_UTIL_H_
+#define UI_GFX_SKIA_UTIL_H_
+#pragma once
+
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkRect.h"
+
+class SkBitmap;
+class SkShader;
+
+namespace gfx {
+
+class Rect;
+
+// Convert between Skia and gfx rect types.
+SkRect RectToSkRect(const gfx::Rect& rect);
+gfx::Rect SkRectToRect(const SkRect& rect);
+
+// Creates a vertical gradient shader. The caller owns the shader.
+// Example usage to avoid leaks:
+// SkSafeUnref(paint.setShader(gfx::CreateGradientShader(0, 10, red, blue)));
+//
+// (The old shader in the paint, if any, needs to be freed, and SkSafeUnref will
+// handle the NULL case.)
+SkShader* CreateGradientShader(int start_point,
+ int end_point,
+ SkColor start_color,
+ SkColor end_color);
+
+// Returns true if the two bitmaps contain the same pixels.
+bool BitmapsAreEqual(const SkBitmap& bitmap1, const SkBitmap& bitmap2);
+
+} // namespace gfx;
+
+#endif // UI_GFX_SKIA_UTIL_H_
diff --git a/ui/gfx/skia_utils_gtk.cc b/ui/gfx/skia_utils_gtk.cc
new file mode 100644
index 0000000..8ed4bec
--- /dev/null
+++ b/ui/gfx/skia_utils_gtk.cc
@@ -0,0 +1,32 @@
+// 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/skia_utils_gtk.h"
+
+#include <gdk/gdkcolor.h>
+
+namespace gfx {
+
+const int kSkiaToGDKMultiplier = 257;
+
+// GDK_COLOR_RGB multiplies by 257 (= 0x10001) to distribute the bits evenly
+// See: http://www.mindcontrol.org/~hplus/graphics/expand-bits.html
+// To get back, we can just right shift by eight
+// (or, formulated differently, i == (i*257)/256 for all i < 256).
+
+SkColor GdkColorToSkColor(GdkColor color) {
+ return SkColorSetRGB(color.red >> 8, color.green >> 8, color.blue >> 8);
+}
+
+GdkColor SkColorToGdkColor(SkColor color) {
+ GdkColor gdk_color = {
+ 0,
+ SkColorGetR(color) * kSkiaToGDKMultiplier,
+ SkColorGetG(color) * kSkiaToGDKMultiplier,
+ SkColorGetB(color) * kSkiaToGDKMultiplier
+ };
+ return gdk_color;
+}
+
+} // namespace gfx
diff --git a/ui/gfx/skia_utils_gtk.h b/ui/gfx/skia_utils_gtk.h
new file mode 100644
index 0000000..5682f09
--- /dev/null
+++ b/ui/gfx/skia_utils_gtk.h
@@ -0,0 +1,23 @@
+// 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.
+
+#ifndef UI_GFX_SKIA_UTILS_GTK_H_
+#define UI_GFX_SKIA_UTILS_GTK_H_
+#pragma once
+
+#include "third_party/skia/include/core/SkColor.h"
+
+typedef struct _GdkColor GdkColor;
+
+namespace gfx {
+
+// Converts GdkColors to the ARGB layout Skia expects.
+SkColor GdkColorToSkColor(GdkColor color);
+
+// Converts ARGB to GdkColor.
+GdkColor SkColorToGdkColor(SkColor color);
+
+} // namespace gfx
+
+#endif // UI_GFX_SKIA_UTILS_GTK_H_
diff --git a/ui/gfx/test_suite.cc b/ui/gfx/test_suite.cc
new file mode 100644
index 0000000..02e1f7f
--- /dev/null
+++ b/ui/gfx/test_suite.cc
@@ -0,0 +1,48 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "gfx/test_suite.h"
+
+#include "gfx/gfx_paths.h"
+#include "base/file_path.h"
+#include "base/path_service.h"
+#include "base/mac/scoped_nsautorelease_pool.h"
+
+#if defined(OS_MACOSX)
+#include "base/mac/mac_util.h"
+#endif
+
+GfxTestSuite::GfxTestSuite(int argc, char** argv) : TestSuite(argc, argv) {}
+
+void GfxTestSuite::Initialize() {
+ base::mac::ScopedNSAutoreleasePool autorelease_pool;
+
+ TestSuite::Initialize();
+
+ gfx::RegisterPathProvider();
+
+#if defined(OS_MACOSX)
+ // Look in the framework bundle for resources.
+ // TODO(port): make a resource bundle for non-app exes. What's done here
+ // isn't really right because this code needs to depend on chrome_dll
+ // being built. This is inappropriate in app.
+ FilePath path;
+ PathService::Get(base::DIR_EXE, &path);
+#if defined(GOOGLE_CHROME_BUILD)
+ path = path.AppendASCII("Google Chrome Framework.framework");
+#elif defined(CHROMIUM_BUILD)
+ path = path.AppendASCII("Chromium Framework.framework");
+#else
+#error Unknown branding
+#endif
+ base::mac::SetOverrideAppBundlePath(path);
+#endif // OS_MACOSX
+}
+
+void GfxTestSuite::Shutdown() {
+#if defined(OS_MACOSX)
+ base::mac::SetOverrideAppBundle(NULL);
+#endif
+ TestSuite::Shutdown();
+}
diff --git a/ui/gfx/test_suite.h b/ui/gfx/test_suite.h
new file mode 100644
index 0000000..488ea31
--- /dev/null
+++ b/ui/gfx/test_suite.h
@@ -0,0 +1,24 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_TEST_SUITE_H_
+#define UI_GFX_TEST_SUITE_H_
+#pragma once
+
+#include <string>
+
+#include "base/test/test_suite.h"
+#include "build/build_config.h"
+
+class GfxTestSuite : public base::TestSuite {
+ public:
+ GfxTestSuite(int argc, char** argv);
+
+ protected:
+ // Overridden from base::TestSuite:
+ virtual void Initialize();
+ virtual void Shutdown();
+};
+
+#endif // UI_GFX_TEST_SUITE_H_
diff --git a/ui/gfx/win_util.cc b/ui/gfx/win_util.cc
new file mode 100644
index 0000000..9941a65
--- /dev/null
+++ b/ui/gfx/win_util.cc
@@ -0,0 +1,63 @@
+// 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/win_util.h"
+
+#include <windows.h>
+
+#include "base/win/windows_version.h"
+
+namespace {
+
+bool DynamicLibraryPresent(const wchar_t* lib_name) {
+ HINSTANCE lib = LoadLibrary(lib_name);
+ if (lib) {
+ FreeLibrary(lib);
+ return true;
+ }
+ return false;
+}
+
+} // namespace
+
+namespace gfx {
+
+// Returns true if Direct2d is available, false otherwise.
+bool Direct2dIsAvailable() {
+ static bool checked = false;
+ static bool available = false;
+
+ if (!checked) {
+ base::win::Version version = base::win::GetVersion();
+ if (version < base::win::VERSION_VISTA)
+ available = false;
+ else if (version >= base::win::VERSION_WIN7)
+ available = true;
+ else
+ available = DynamicLibraryPresent(L"d2d1.dll");
+ checked = true;
+ }
+ return available;
+}
+
+// Returns true if DirectWrite is available, false otherwise.
+bool DirectWriteIsAvailable() {
+ static bool checked = false;
+ static bool available = false;
+
+ if (!checked) {
+ base::win::Version version = base::win::GetVersion();
+ if (version < base::win::VERSION_VISTA)
+ available = false;
+ else if (version >= base::win::VERSION_WIN7)
+ available = true;
+ else
+ available = DynamicLibraryPresent(L"dwrite.dll");
+ checked = true;
+ }
+ return available;
+}
+
+} // namespace gfx
+
diff --git a/ui/gfx/win_util.h b/ui/gfx/win_util.h
new file mode 100644
index 0000000..3414f13
--- /dev/null
+++ b/ui/gfx/win_util.h
@@ -0,0 +1,20 @@
+// 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.
+
+#ifndef UI_GFX_WIN_UTIL_H_
+#define UI_GFX_WIN_UTIL_H_
+#pragma once
+
+namespace gfx {
+
+// Returns true if Direct2d is available, false otherwise.
+bool Direct2dIsAvailable();
+
+// Returns true if DirectWrite is available, false otherwise.
+bool DirectWriteIsAvailable();
+
+} // namespace gfx;
+
+#endif // UI_GFX_WIN_UTIL_H_
+