// 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 #include #include #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::iterator it = cursor_cache_.begin(); it != cursor_cache_.end(); ++it) { gdk_cursor_unref(it->second); } cursor_cache_.clear(); } GdkCursor* GetCursorImpl(GdkCursorType type) { std::map::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 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& args = command_line.argv(); int argc = args.size(); scoped_array 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(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& 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); #if !defined(OS_CHROMEOS) // Until we switch to vector graphics, force the max DPI to 96.0. resolution = std::min(resolution, 96.); #endif g_object_unref(default_context); } return resolution; } GdkCursor* GetCursor(int type) { static GdkCursorCache impl; return impl.GetCursorImpl(static_cast(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(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