gtk: Use libgtk2ui from chrome.

This commit is contained in:
Cheng Zhao 2014-06-29 03:41:22 +00:00
parent 436deddf68
commit e5c10f29de
12 changed files with 624 additions and 16 deletions

View file

@ -234,6 +234,13 @@
'chrome/browser/ui/gtk/gtk_window_util.h',
'chrome/browser/ui/gtk/menu_gtk.cc',
'chrome/browser/ui/gtk/menu_gtk.h',
'chrome/browser/ui/libgtk2ui/g_object_destructor_filo.cc',
'chrome/browser/ui/libgtk2ui/g_object_destructor_filo.h',
'chrome/browser/ui/libgtk2ui/gtk2_signal.h',
'chrome/browser/ui/libgtk2ui/gtk2_signal_registrar.cc',
'chrome/browser/ui/libgtk2ui/gtk2_signal_registrar.h',
'chrome/browser/ui/libgtk2ui/skia_utils_gtk2.cc',
'chrome/browser/ui/libgtk2ui/skia_utils_gtk2.h',
'chrome/browser/ui/views/status_icons/status_tray_state_changer_win.cc',
'chrome/browser/ui/views/status_icons/status_tray_state_changer_win.h',
'<@(native_mate_files)',

View file

@ -12,6 +12,7 @@
#include "base/environment.h"
#include "base/nix/xdg_util.h"
#include "chrome/browser/ui/gtk/gtk_window_util.h"
#include "chrome/browser/ui/libgtk2ui/skia_utils_gtk2.h"
#include "content/public/browser/native_web_keyboard_event.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_view.h"
@ -25,7 +26,6 @@
#include "ui/gfx/gtk_util.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/rect.h"
#include "ui/gfx/skia_utils_gtk.h"
namespace atom {
@ -131,7 +131,8 @@ NativeWindowGtk::NativeWindowGtk(content::WebContents* web_contents,
gtk_widget_realize(GTK_WIDGET(window_));
if (icon_)
gtk_window_set_icon(window_, icon_->ToGdkPixbuf());
gtk_window_set_icon(window_,
libgtk2ui::GdkPixbufFromSkBitmap(*icon_->ToSkBitmap()));
ui::ActiveWindowWatcherX::AddObserver(this);
@ -443,7 +444,7 @@ void NativeWindowGtk::SetWebKitColorStyle() {
GetWebContents()->GetMutableRendererPrefs();
GtkStyle* frame_style = gtk_rc_get_style(GTK_WIDGET(window_));
prefs->focus_ring_color =
gfx::GdkColorToSkColor(frame_style->bg[GTK_STATE_SELECTED]);
libgtk2ui::GdkColorToSkColor(frame_style->bg[GTK_STATE_SELECTED]);
prefs->thumb_active_color = SkColorSetRGB(244, 244, 244);
prefs->thumb_inactive_color = SkColorSetRGB(234, 234, 234);
prefs->track_color = SkColorSetRGB(211, 211, 211);
@ -451,13 +452,13 @@ void NativeWindowGtk::SetWebKitColorStyle() {
GtkWidget* url_entry = gtk_entry_new();
GtkStyle* entry_style = gtk_rc_get_style(url_entry);
prefs->active_selection_bg_color =
gfx::GdkColorToSkColor(entry_style->base[GTK_STATE_SELECTED]);
libgtk2ui::GdkColorToSkColor(entry_style->base[GTK_STATE_SELECTED]);
prefs->active_selection_fg_color =
gfx::GdkColorToSkColor(entry_style->text[GTK_STATE_SELECTED]);
libgtk2ui::GdkColorToSkColor(entry_style->text[GTK_STATE_SELECTED]);
prefs->inactive_selection_bg_color =
gfx::GdkColorToSkColor(entry_style->base[GTK_STATE_ACTIVE]);
libgtk2ui::GdkColorToSkColor(entry_style->base[GTK_STATE_ACTIVE]);
prefs->inactive_selection_fg_color =
gfx::GdkColorToSkColor(entry_style->text[GTK_STATE_ACTIVE]);
libgtk2ui::GdkColorToSkColor(entry_style->text[GTK_STATE_ACTIVE]);
gtk_widget_destroy(url_entry);
const base::TimeDelta cursor_blink_time = gfx::GetCursorBlinkCycle();

View file

@ -17,7 +17,7 @@ class AtomBindings {
AtomBindings();
virtual ~AtomBindings();
// Add process.atom_binding function, which behaves like process.atomBinding but
// Add process.atomBinding function, which behaves like process.binding but
// load native code from atom-shell instead.
virtual void BindTo(v8::Isolate* isolate, v8::Handle<v8::Object> process);

View file

@ -16,6 +16,7 @@
#include "chrome/browser/ui/gtk/gtk_custom_menu.h"
#include "chrome/browser/ui/gtk/gtk_custom_menu_item.h"
#include "chrome/browser/ui/gtk/gtk_util.h"
#include "chrome/browser/ui/libgtk2ui/skia_utils_gtk2.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/accelerators/menu_label_accelerator_util_linux.h"
#include "ui/base/accelerators/platform_accelerator_gtk.h"
@ -350,8 +351,10 @@ GtkWidget* MenuGtk::BuildMenuItemWithImage(const std::string& label,
GtkWidget* MenuGtk::BuildMenuItemWithImage(const std::string& label,
const gfx::Image& icon) {
GtkWidget* menu_item = BuildMenuItemWithImage(label,
gtk_image_new_from_pixbuf(icon.ToGdkPixbuf()));
GtkWidget* menu_item = BuildMenuItemWithImage(
label,
gtk_image_new_from_pixbuf(
libgtk2ui::GdkPixbufFromSkBitmap(*icon.ToSkBitmap())));
return menu_item;
}
@ -856,9 +859,10 @@ void MenuGtk::SetMenuItemInfo(GtkWidget* widget, gpointer userdata) {
if (GTK_IS_IMAGE_MENU_ITEM(widget)) {
gfx::Image icon;
if (model->GetIconAt(id, &icon)) {
gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(widget),
gtk_image_new_from_pixbuf(
icon.ToGdkPixbuf()));
gtk_image_menu_item_set_image(
GTK_IMAGE_MENU_ITEM(widget),
gtk_image_new_from_pixbuf(
libgtk2ui::GdkPixbufFromSkBitmap(*icon.ToSkBitmap())));
} else {
gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(widget), NULL);
}

View file

@ -11,8 +11,8 @@
#include <vector>
#include "base/memory/weak_ptr.h"
#include "ui/base/gtk/gtk_signal.h"
#include "ui/base/gtk/gtk_signal_registrar.h"
#include "chrome/browser/ui/libgtk2ui/gtk2_signal.h"
#include "chrome/browser/ui/libgtk2ui/gtk2_signal_registrar.h"
#include "ui/gfx/point.h"
namespace gfx {
@ -216,7 +216,7 @@ class MenuGtk {
// menu.
static bool block_activation_;
ui::GtkSignalRegistrar signal_;
libgtk2ui::Gtk2SignalRegistrar signal_;
base::WeakPtrFactory<MenuGtk> weak_factory_;
};

View file

@ -0,0 +1,89 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/libgtk2ui/g_object_destructor_filo.h"
#include <glib-object.h>
#include "base/logging.h"
#include "base/memory/singleton.h"
namespace libgtk2ui {
GObjectDestructorFILO::GObjectDestructorFILO() {
}
GObjectDestructorFILO::~GObjectDestructorFILO() {
// Probably CHECK(handler_map_.empty()) would look natural here. But
// some tests (some views_unittests) violate this assertion.
}
// static
GObjectDestructorFILO* GObjectDestructorFILO::GetInstance() {
return Singleton<GObjectDestructorFILO>::get();
}
void GObjectDestructorFILO::Connect(
GObject* object, DestructorHook callback, void* context) {
const Hook hook(object, callback, context);
HandlerMap::iterator iter = handler_map_.find(object);
if (iter == handler_map_.end()) {
g_object_weak_ref(object, WeakNotifyThunk, this);
handler_map_[object].push_front(hook);
} else {
iter->second.push_front(hook);
}
}
void GObjectDestructorFILO::Disconnect(
GObject* object, DestructorHook callback, void* context) {
HandlerMap::iterator iter = handler_map_.find(object);
if (iter == handler_map_.end()) {
LOG(DFATAL) << "Unable to disconnect destructor hook for object " << object
<< ": hook not found (" << callback << ", " << context << ").";
return;
}
HandlerList& dtors = iter->second;
if (dtors.empty()) {
LOG(DFATAL) << "Destructor list is empty for specified object " << object
<< " Maybe it is being executed?";
return;
}
if (!dtors.front().equal(object, callback, context)) {
// Reenable this warning once this bug is fixed:
// http://code.google.com/p/chromium/issues/detail?id=85603
DVLOG(1) << "Destructors should be unregistered the reverse order they "
<< "were registered. But for object " << object << " "
<< "deleted hook is "<< context << ", the last queued hook is "
<< dtors.front().context;
}
for (HandlerList::iterator i = dtors.begin(); i != dtors.end(); ++i) {
if (i->equal(object, callback, context)) {
dtors.erase(i);
break;
}
}
if (dtors.empty()) {
g_object_weak_unref(object, WeakNotifyThunk, this);
handler_map_.erase(iter);
}
}
void GObjectDestructorFILO::WeakNotify(GObject* where_the_object_was) {
HandlerMap::iterator iter = handler_map_.find(where_the_object_was);
DCHECK(iter != handler_map_.end());
DCHECK(!iter->second.empty());
// Save destructor list for given object into local copy to avoid reentrancy
// problem: if callee wants to modify the caller list.
HandlerList dtors;
iter->second.swap(dtors);
handler_map_.erase(iter);
// Execute hooks in local list in FILO order.
for (HandlerList::iterator i = dtors.begin(); i != dtors.end(); ++i)
i->callback(i->context, where_the_object_was);
}
} // namespace libgtk2ui

View file

@ -0,0 +1,90 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_LIBGTK2UI_G_OBJECT_DESTRUCTOR_FILO_H_
#define CHROME_BROWSER_UI_LIBGTK2UI_G_OBJECT_DESTRUCTOR_FILO_H_
#include <glib.h>
#include <list>
#include <map>
#include "base/basictypes.h"
template <typename T> struct DefaultSingletonTraits;
typedef struct _GObject GObject;
namespace libgtk2ui {
// This class hooks calls to g_object_weak_ref()/unref() and executes them in
// FILO order. This is important if there are several hooks to the single object
// (set up at different levels of class hierarchy) and the lowest hook (set up
// first) is deleting self - it must be called last (among hooks for the given
// object). Unfortunately Glib does not provide this guarantee.
//
// Use it as follows:
//
// static void OnDestroyedThunk(gpointer data, GObject *where_the_object_was) {
// reinterpret_cast<MyClass*>(data)->OnDestroyed(where_the_object_was);
// }
// void MyClass::OnDestroyed(GObject *where_the_object_was) {
// destroyed_ = true;
// delete this;
// }
// MyClass::Init() {
// ...
// ui::GObjectDestructorFILO::GetInstance()->Connect(
// G_OBJECT(my_widget), &OnDestroyedThunk, this);
// }
// MyClass::~MyClass() {
// if (!destroyed_) {
// ui::GObjectDestructorFILO::GetInstance()->Disconnect(
// G_OBJECT(my_widget), &OnDestroyedThunk, this);
// }
// }
//
// TODO(glotov): Probably worth adding ScopedGObjectDtor<T>.
//
// This class is a singleton. Not thread safe. Must be called within UI thread.
class GObjectDestructorFILO {
public:
typedef void (*DestructorHook)(void* context, GObject* where_the_object_was);
static GObjectDestructorFILO* GetInstance();
void Connect(GObject* object, DestructorHook callback, void* context);
void Disconnect(GObject* object, DestructorHook callback, void* context);
private:
struct Hook {
Hook(GObject* o, DestructorHook cb, void* ctx)
: object(o), callback(cb), context(ctx) {
}
bool equal(GObject* o, DestructorHook cb, void* ctx) const {
return object == o && callback == cb && context == ctx;
}
GObject* object;
DestructorHook callback;
void* context;
};
typedef std::list<Hook> HandlerList;
typedef std::map<GObject*, HandlerList> HandlerMap;
GObjectDestructorFILO();
~GObjectDestructorFILO();
friend struct DefaultSingletonTraits<GObjectDestructorFILO>;
void WeakNotify(GObject* where_the_object_was);
static void WeakNotifyThunk(gpointer data, GObject* where_the_object_was) {
reinterpret_cast<GObjectDestructorFILO*>(data)->WeakNotify(
where_the_object_was);
}
HandlerMap handler_map_;
DISALLOW_COPY_AND_ASSIGN(GObjectDestructorFILO);
};
} // namespace libgtk2ui
#endif // CHROME_BROWSER_UI_LIBGTK2UI_G_OBJECT_DESTRUCTOR_FILO_H_

View file

@ -0,0 +1,68 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_LIBGTK2UI_GTK2_SIGNAL_H_
#define CHROME_BROWSER_UI_LIBGTK2UI_GTK2_SIGNAL_H_
#include "ui/base/glib/glib_signal.h"
typedef struct _GtkWidget GtkWidget;
// These macros handle the common case where the sender object will be a
// GtkWidget*.
#define CHROMEGTK_CALLBACK_0(CLASS, RETURN, METHOD) \
CHROMEG_CALLBACK_0(CLASS, RETURN, METHOD, GtkWidget*);
#define CHROMEGTK_CALLBACK_1(CLASS, RETURN, METHOD, ARG1) \
CHROMEG_CALLBACK_1(CLASS, RETURN, METHOD, GtkWidget*, ARG1);
#define CHROMEGTK_CALLBACK_2(CLASS, RETURN, METHOD, ARG1, ARG2) \
CHROMEG_CALLBACK_2(CLASS, RETURN, METHOD, GtkWidget*, ARG1, ARG2);
#define CHROMEGTK_CALLBACK_3(CLASS, RETURN, METHOD, ARG1, ARG2, ARG3) \
CHROMEG_CALLBACK_3(CLASS, RETURN, METHOD, GtkWidget*, ARG1, ARG2, ARG3);
#define CHROMEGTK_CALLBACK_4(CLASS, RETURN, METHOD, ARG1, ARG2, ARG3, ARG4) \
CHROMEG_CALLBACK_4(CLASS, RETURN, METHOD, GtkWidget*, ARG1, ARG2, ARG3, \
ARG4);
#define CHROMEGTK_CALLBACK_5(CLASS, RETURN, METHOD, ARG1, ARG2, ARG3, ARG4, \
ARG5) \
CHROMEG_CALLBACK_5(CLASS, RETURN, METHOD, GtkWidget*, ARG1, ARG2, ARG3, \
ARG4, ARG5);
#define CHROMEGTK_CALLBACK_6(CLASS, RETURN, METHOD, ARG1, ARG2, ARG3, ARG4, \
ARG5, ARG6) \
CHROMEG_CALLBACK_6(CLASS, RETURN, METHOD, GtkWidget*, ARG1, ARG2, ARG3, \
ARG4, ARG5, ARG6);
#define CHROMEGTK_VIRTUAL_CALLBACK_0(CLASS, RETURN, METHOD) \
CHROMEG_VIRTUAL_CALLBACK_0(CLASS, RETURN, METHOD, GtkWidget*);
#define CHROMEGTK_VIRTUAL_CALLBACK_1(CLASS, RETURN, METHOD, ARG1) \
CHROMEG_VIRTUAL_CALLBACK_1(CLASS, RETURN, METHOD, GtkWidget*, ARG1);
#define CHROMEGTK_VIRTUAL_CALLBACK_2(CLASS, RETURN, METHOD, ARG1, ARG2) \
CHROMEG_VIRTUAL_CALLBACK_2(CLASS, RETURN, METHOD, GtkWidget*, ARG1, ARG2);
#define CHROMEGTK_VIRTUAL_CALLBACK_3(CLASS, RETURN, METHOD, ARG1, ARG2, ARG3) \
CHROMEG_VIRTUAL_CALLBACK_3(CLASS, RETURN, METHOD, GtkWidget*, ARG1, ARG2, \
ARG3);
#define CHROMEGTK_VIRTUAL_CALLBACK_4(CLASS, RETURN, METHOD, ARG1, ARG2, ARG3, \
ARG4) \
CHROMEG_VIRTUAL_CALLBACK_4(CLASS, RETURN, METHOD, GtkWidget*, ARG1, ARG2, \
ARG3, ARG4);
#define CHROMEGTK_VIRTUAL_CALLBACK_5(CLASS, RETURN, METHOD, ARG1, ARG2, ARG3, \
ARG4, ARG5) \
CHROMEG_VIRTUAL_CALLBACK_5(CLASS, RETURN, METHOD, GtkWidget*, ARG1, ARG2, \
ARG3, ARG4, ARG5);
#define CHROMEGTK_VIRTUAL_CALLBACK_6(CLASS, RETURN, METHOD, ARG1, ARG2, ARG3, \
ARG4, ARG5, ARG6) \
CHROMEG_VIRTUAL_CALLBACK_6(CLASS, RETURN, METHOD, GtkWidget*, ARG1, ARG2, \
ARG3, ARG4, ARG5, ARG6);
#endif // CHROME_BROWSER_UI_LIBGTK2UI_GTK2_SIGNAL_H_

View file

@ -0,0 +1,98 @@
// 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 "chrome/browser/ui/libgtk2ui/gtk2_signal_registrar.h"
#include <glib-object.h>
#include "base/logging.h"
#include "chrome/browser/ui/libgtk2ui/g_object_destructor_filo.h"
namespace libgtk2ui {
Gtk2SignalRegistrar::Gtk2SignalRegistrar() {
}
Gtk2SignalRegistrar::~Gtk2SignalRegistrar() {
for (HandlerMap::iterator list_iter = handler_lists_.begin();
list_iter != handler_lists_.end(); ++list_iter) {
GObject* object = list_iter->first;
GObjectDestructorFILO::GetInstance()->Disconnect(
object, WeakNotifyThunk, this);
HandlerList& handlers = list_iter->second;
for (HandlerList::iterator ids_iter = handlers.begin();
ids_iter != handlers.end(); ++ids_iter) {
g_signal_handler_disconnect(object, *ids_iter);
}
}
}
glong Gtk2SignalRegistrar::Connect(gpointer instance,
const gchar* detailed_signal,
GCallback signal_handler,
gpointer data) {
return ConnectInternal(instance, detailed_signal, signal_handler, data,
false);
}
glong Gtk2SignalRegistrar::ConnectAfter(gpointer instance,
const gchar* detailed_signal,
GCallback signal_handler,
gpointer data) {
return ConnectInternal(instance, detailed_signal, signal_handler, data, true);
}
glong Gtk2SignalRegistrar::ConnectInternal(gpointer instance,
const gchar* detailed_signal,
GCallback signal_handler,
gpointer data,
bool after) {
GObject* object = G_OBJECT(instance);
HandlerMap::iterator iter = handler_lists_.find(object);
if (iter == handler_lists_.end()) {
GObjectDestructorFILO::GetInstance()->Connect(
object, WeakNotifyThunk, this);
handler_lists_[object] = HandlerList();
iter = handler_lists_.find(object);
}
glong handler_id = after ?
g_signal_connect_after(instance, detailed_signal, signal_handler, data) :
g_signal_connect(instance, detailed_signal, signal_handler, data);
iter->second.push_back(handler_id);
return handler_id;
}
void Gtk2SignalRegistrar::WeakNotify(GObject* where_the_object_was) {
HandlerMap::iterator iter = handler_lists_.find(where_the_object_was);
if (iter == handler_lists_.end()) {
NOTREACHED();
return;
}
// The signal handlers will be disconnected automatically. Just erase the
// handler id list.
handler_lists_.erase(iter);
}
void Gtk2SignalRegistrar::DisconnectAll(gpointer instance) {
GObject* object = G_OBJECT(instance);
HandlerMap::iterator iter = handler_lists_.find(object);
if (iter == handler_lists_.end())
return;
GObjectDestructorFILO::GetInstance()->Disconnect(
object, WeakNotifyThunk, this);
HandlerList& handlers = iter->second;
for (HandlerList::iterator ids_iter = handlers.begin();
ids_iter != handlers.end(); ++ids_iter) {
g_signal_handler_disconnect(object, *ids_iter);
}
handler_lists_.erase(iter);
}
} // namespace libgtk2ui

View file

@ -0,0 +1,74 @@
// 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 CHROME_BROWSER_UI_LIBGTK2UI_GTK2_SIGNAL_REGISTRAR_H_
#define CHROME_BROWSER_UI_LIBGTK2UI_GTK2_SIGNAL_REGISTRAR_H_
#include <glib.h>
#include <map>
#include <vector>
#include "base/basictypes.h"
typedef void (*GCallback) (void);
typedef struct _GObject GObject;
typedef struct _GtkWidget GtkWidget;
namespace libgtk2ui {
// A class that ensures that callbacks don't run on stale owner objects. Similar
// in spirit to NotificationRegistrar. Use as follows:
//
// class ChromeObject {
// public:
// ChromeObject() {
// ...
//
// signals_.Connect(widget, "event", CallbackThunk, this);
// }
//
// ...
//
// private:
// Gtk2SignalRegistrar signals_;
// };
//
// When |signals_| goes down, it will disconnect the handlers connected via
// Connect.
class Gtk2SignalRegistrar {
public:
Gtk2SignalRegistrar();
~Gtk2SignalRegistrar();
// Connect before the default handler. Returns the handler id.
glong Connect(gpointer instance, const gchar* detailed_signal,
GCallback signal_handler, gpointer data);
// Connect after the default handler. Returns the handler id.
glong ConnectAfter(gpointer instance, const gchar* detailed_signal,
GCallback signal_handler, gpointer data);
// Disconnects all signal handlers connected to |instance|.
void DisconnectAll(gpointer instance);
private:
typedef std::vector<glong> HandlerList;
typedef std::map<GObject*, HandlerList> HandlerMap;
static void WeakNotifyThunk(gpointer data, GObject* where_the_object_was) {
reinterpret_cast<Gtk2SignalRegistrar*>(data)->WeakNotify(
where_the_object_was);
}
void WeakNotify(GObject* where_the_object_was);
glong ConnectInternal(gpointer instance, const gchar* detailed_signal,
GCallback signal_handler, gpointer data, bool after);
HandlerMap handler_lists_;
DISALLOW_COPY_AND_ASSIGN(Gtk2SignalRegistrar);
};
} // namespace libgtk2ui
#endif // CHROME_BROWSER_UI_LIBGTK2UI_GTK2_SIGNAL_REGISTRAR_H_

View file

@ -0,0 +1,135 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/libgtk2ui/skia_utils_gtk2.h"
#include <gdk/gdk.h>
#include "base/basictypes.h"
#include "base/logging.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkUnPreMultiply.h"
namespace libgtk2ui {
// 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,
static_cast<guint16>(SkColorGetR(color) * kSkiaToGDKMultiplier),
static_cast<guint16>(SkColorGetG(color) * kSkiaToGDKMultiplier),
static_cast<guint16>(SkColorGetB(color) * kSkiaToGDKMultiplier)
};
return gdk_color;
}
const SkBitmap GdkPixbufToImageSkia(GdkPixbuf* pixbuf) {
// TODO(erg): What do we do in the case where the pixbuf fails these dchecks?
// I would prefer to use our gtk based canvas, but that would require
// recompiling half of our skia extensions with gtk support, which we can't
// do in this build.
DCHECK_EQ(GDK_COLORSPACE_RGB, gdk_pixbuf_get_colorspace(pixbuf));
int n_channels = gdk_pixbuf_get_n_channels(pixbuf);
int w = gdk_pixbuf_get_width(pixbuf);
int h = gdk_pixbuf_get_height(pixbuf);
SkBitmap ret;
ret.setConfig(SkBitmap::kARGB_8888_Config, w, h);
ret.allocPixels();
ret.eraseColor(0);
uint32_t* skia_data = static_cast<uint32_t*>(ret.getAddr(0, 0));
if (n_channels == 4) {
int total_length = w * h;
guchar* gdk_pixels = gdk_pixbuf_get_pixels(pixbuf);
// Now here's the trick: we need to convert the gdk data (which is RGBA and
// isn't premultiplied) to skia (which can be anything and premultiplied).
for (int i = 0; i < total_length; ++i, gdk_pixels += 4) {
const unsigned char& red = gdk_pixels[0];
const unsigned char& green = gdk_pixels[1];
const unsigned char& blue = gdk_pixels[2];
const unsigned char& alpha = gdk_pixels[3];
skia_data[i] = SkPreMultiplyARGB(alpha, red, green, blue);
}
} else if (n_channels == 3) {
// Because GDK makes rowstrides word aligned, we need to do something a bit
// more complex when a pixel isn't perfectly a word of memory.
int rowstride = gdk_pixbuf_get_rowstride(pixbuf);
guchar* gdk_pixels = gdk_pixbuf_get_pixels(pixbuf);
for (int y = 0; y < h; ++y) {
int row = y * rowstride;
for (int x = 0; x < w; ++x) {
guchar* pixel = gdk_pixels + row + (x * 3);
const unsigned char& red = pixel[0];
const unsigned char& green = pixel[1];
const unsigned char& blue = pixel[2];
skia_data[y * w + x] = SkPreMultiplyARGB(255, red, green, blue);
}
}
} else {
NOTREACHED();
}
return ret;
}
GdkPixbuf* GdkPixbufFromSkBitmap(const SkBitmap& bitmap) {
if (bitmap.isNull())
return NULL;
SkAutoLockPixels lock_pixels(bitmap);
int width = bitmap.width();
int height = bitmap.height();
GdkPixbuf* pixbuf =
gdk_pixbuf_new(GDK_COLORSPACE_RGB, // The only colorspace gtk supports.
TRUE, // There is an alpha channel.
8,
width,
height);
// SkBitmaps are premultiplied, we need to unpremultiply them.
const int kBytesPerPixel = 4;
uint8* divided = gdk_pixbuf_get_pixels(pixbuf);
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;
}
}
return pixbuf;
}
} // namespace libgtk2ui

View file

@ -0,0 +1,42 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_LIBGTK2UI_SKIA_UTILS_GTK2_H_
#define CHROME_BROWSER_UI_LIBGTK2UI_SKIA_UTILS_GTK2_H_
#include "third_party/skia/include/core/SkColor.h"
typedef struct _GdkColor GdkColor;
typedef struct _GdkPixbuf GdkPixbuf;
class SkBitmap;
// Define a macro for creating GdkColors from RGB values. This is a macro to
// allow static construction of literals, etc. Use this like:
// GdkColor white = GDK_COLOR_RGB(0xff, 0xff, 0xff);
#define GDK_COLOR_RGB(r, g, b) {0, r * ::libgtk2ui::kSkiaToGDKMultiplier, \
g * ::libgtk2ui::kSkiaToGDKMultiplier, \
b * ::libgtk2ui::kSkiaToGDKMultiplier}
namespace libgtk2ui {
// Multiply uint8 color components by this.
const int kSkiaToGDKMultiplier = 257;
// Converts GdkColors to the ARGB layout Skia expects.
SkColor GdkColorToSkColor(GdkColor color);
// Converts ARGB to GdkColor.
GdkColor SkColorToGdkColor(SkColor color);
const SkBitmap GdkPixbufToImageSkia(GdkPixbuf* pixbuf);
// 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);
} // namespace libgtk2ui
#endif // CHROME_BROWSER_UI_LIBGTK2UI_SKIA_UTILS_GTK2_H_