mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
bug 853682 - remove most of the chromium gfx stuff since it isn't used we need to keep a few headers because they're included, but we don't even need to define the functions they declare. r=bsmedberg
This commit is contained in:
parent
03a78d8a30
commit
57a2df4dd8
@ -1,79 +0,0 @@
|
||||
// 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/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
|
@ -1,145 +0,0 @@
|
||||
// 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 "base/gfx/gtk_native_view_id_manager.h"
|
||||
|
||||
#include "base/gfx/rect.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/rand_util.h"
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <gdk/gdkx.h>
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Bounce functions for GTK to callback into a C++ object...
|
||||
|
||||
static void OnRealize(gfx::NativeView widget, void* arg) {
|
||||
GtkNativeViewManager* manager = reinterpret_cast<GtkNativeViewManager*>(arg);
|
||||
manager->OnRealize(widget);
|
||||
}
|
||||
|
||||
static 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() {
|
||||
}
|
||||
|
||||
gfx::NativeViewId GtkNativeViewManager::GetIdForWidget(gfx::NativeView widget) {
|
||||
// This is just for unit tests:
|
||||
if (!widget)
|
||||
return 0;
|
||||
|
||||
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;
|
||||
if (GTK_WIDGET_REALIZED(widget)) {
|
||||
GdkWindow *gdk_window = widget->window;
|
||||
CHECK(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) {
|
||||
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;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// 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) {
|
||||
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) {
|
||||
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());
|
||||
|
||||
i->second.x_window_id = 0;
|
||||
}
|
||||
|
||||
void GtkNativeViewManager::OnDestroy(gfx::NativeView widget) {
|
||||
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());
|
||||
|
||||
native_view_to_id_.erase(i);
|
||||
id_to_info_.erase(j);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
@ -1,91 +0,0 @@
|
||||
// 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 BASE_GFX_GTK_NATIVE_VIEW_ID_MANAGER_H_
|
||||
#define BASE_GFX_GTK_NATIVE_VIEW_ID_MANAGER_H_
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "base/singleton.h"
|
||||
#include "base/gfx/native_widget_types.h"
|
||||
|
||||
typedef unsigned long XID;
|
||||
|
||||
// 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.
|
||||
//
|
||||
// You get a reference to the global instance with:
|
||||
// Singleton<GtkNativeViewManager>()
|
||||
class GtkNativeViewManager {
|
||||
public:
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
|
||||
private:
|
||||
// This object is a singleton:
|
||||
GtkNativeViewManager();
|
||||
friend struct DefaultSingletonTraits<GtkNativeViewManager>;
|
||||
|
||||
struct NativeViewInfo {
|
||||
NativeViewInfo()
|
||||
: x_window_id(0) {
|
||||
}
|
||||
|
||||
XID x_window_id;
|
||||
};
|
||||
|
||||
gfx::NativeViewId GetWidgetId(gfx::NativeView id);
|
||||
|
||||
// protects native_view_to_id_ and id_to_info_
|
||||
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_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(GtkNativeViewManager);
|
||||
};
|
||||
|
||||
#endif // BASE_GFX_GTK_NATIVE_VIEW_ID_MANAGER_H_
|
@ -1,30 +0,0 @@
|
||||
// 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 "base/gfx/gtk_util.h"
|
||||
|
||||
#include <gdk/gdk.h>
|
||||
#include <gtk/gtk.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "base/gfx/rect.h"
|
||||
|
||||
namespace gfx {
|
||||
|
||||
const GdkColor kGdkWhite = GDK_COLOR_RGB(0xff, 0xff, 0xff);
|
||||
const GdkColor kGdkBlack = GDK_COLOR_RGB(0x00, 0x00, 0x00);
|
||||
const GdkColor kGdkGreen = GDK_COLOR_RGB(0x00, 0xff, 0x00);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gfx
|
@ -1,57 +0,0 @@
|
||||
// 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 BASE_GFX_GTK_UTIL_H_
|
||||
#define BASE_GFX_GTK_UTIL_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
#include "base/scoped_ptr.h"
|
||||
|
||||
typedef struct _GdkColor GdkColor;
|
||||
typedef struct _GdkRegion GdkRegion;
|
||||
|
||||
// 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 * 257, g * 257, b * 257}
|
||||
|
||||
namespace gfx {
|
||||
|
||||
class Rect;
|
||||
|
||||
extern const GdkColor kGdkWhite;
|
||||
extern const GdkColor kGdkBlack;
|
||||
extern const GdkColor kGdkGreen;
|
||||
|
||||
// Modify the given region by subtracting the given rectangles.
|
||||
void SubtractRectanglesFromRegion(GdkRegion* region,
|
||||
const std::vector<Rect>& cutouts);
|
||||
|
||||
} // 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 // BASE_GFX_GTK_UTIL_H_
|
@ -1,523 +0,0 @@
|
||||
// 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/gfx/jpeg_codec.h"
|
||||
|
||||
#include <setjmp.h>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/scoped_ptr.h"
|
||||
#include "skia/include/SkBitmap.h"
|
||||
|
||||
extern "C" {
|
||||
#include "third_party/libjpeg/jpeglib.h"
|
||||
}
|
||||
|
||||
// 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.
|
||||
const static int initial_output_buffer_size = 8192;
|
||||
|
||||
struct JpegEncoderState {
|
||||
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;
|
||||
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);
|
||||
destroyer.SetManagedObject(&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) {
|
||||
converter = StripAlpha;
|
||||
} else if (format == FORMAT_BGRA) {
|
||||
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;
|
||||
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);
|
||||
destroyer.SetManagedObject(&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) {
|
||||
row_write_stride = cinfo.output_width * 4;
|
||||
converter = AddAlpha;
|
||||
} else if (format == FORMAT_BGRA) {
|
||||
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;
|
||||
// Use FORMAT_BGRA as that maps to Skia's 32 bit (kARGB_8888_Config) format.
|
||||
if (!Decode(input, input_size, FORMAT_BGRA, &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;
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
// 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 BASE_GFX_JPEG_CODEC_H_
|
||||
#define BASE_GFX_JPEG_CODEC_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
class SkBitmap;
|
||||
|
||||
// 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
|
||||
};
|
||||
|
||||
// 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);
|
||||
};
|
||||
|
||||
#endif // BASE_GFX_JPEG_CODEC_H_
|
@ -1,147 +0,0 @@
|
||||
// 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 "base/gfx/jpeg_codec.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
// 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));
|
||||
}
|
@ -1,710 +0,0 @@
|
||||
// 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/gfx/native_theme.h"
|
||||
|
||||
#include <windows.h>
|
||||
#include <uxtheme.h>
|
||||
#include <vsstyle.h>
|
||||
#include <vssym32.h>
|
||||
|
||||
#include "base/gfx/gdi_util.h"
|
||||
#include "base/gfx/rect.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/scoped_handle.h"
|
||||
#include "skia/ext/platform_canvas.h"
|
||||
#include "skia/ext/skia_utils_win.h"
|
||||
#include "skia/include/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);
|
||||
paint->setShader(shader)->safeUnref();
|
||||
}
|
||||
|
||||
} // 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,
|
||||
bool is_highlighted) 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);
|
||||
ScopedHDC mem_dc(CreateCompatibleDC(hdc));
|
||||
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, is_highlighted);
|
||||
}
|
||||
|
||||
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,
|
||||
bool is_highlighted) 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, is_highlighted);
|
||||
}
|
||||
|
||||
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::PlatformCanvasWin* 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::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::PlatformCanvasWin* 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;
|
||||
}
|
||||
|
||||
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,
|
||||
bool is_highlighted) const {
|
||||
const int width = rect->right - rect->left;
|
||||
const int height = rect->bottom - rect->top;
|
||||
|
||||
// DrawFrameControl for menu arrow/check wants a monochrome bitmap.
|
||||
ScopedBitmap mask_bitmap(CreateBitmap(width, height, 1, 1, NULL));
|
||||
|
||||
if (mask_bitmap == NULL)
|
||||
return E_OUTOFMEMORY;
|
||||
|
||||
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.
|
||||
COLORREF old_bg_color =
|
||||
SetBkColor(hdc,
|
||||
GetSysColor(is_highlighted ? COLOR_HIGHLIGHT : COLOR_MENU));
|
||||
COLORREF old_text_color =
|
||||
SetTextColor(hdc,
|
||||
GetSysColor(is_highlighted ? COLOR_HIGHLIGHTTEXT :
|
||||
COLOR_MENUTEXT));
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
default:
|
||||
NOTREACHED();
|
||||
}
|
||||
theme_handles_[theme_name] = handle;
|
||||
return handle;
|
||||
}
|
||||
|
||||
} // namespace gfx
|
@ -1,296 +0,0 @@
|
||||
// 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.
|
||||
//
|
||||
// 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 BASE_GFX_NATIVE_THEME_H__
|
||||
#define BASE_GFX_NATIVE_THEME_H__
|
||||
|
||||
#include <windows.h>
|
||||
#include <uxtheme.h>
|
||||
#include "base/basictypes.h"
|
||||
#include "base/gfx/size.h"
|
||||
#include "skia/include/SkColor.h"
|
||||
|
||||
namespace skia {
|
||||
class PlatformCanvasWin;
|
||||
} // 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,
|
||||
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
|
||||
};
|
||||
|
||||
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,
|
||||
bool is_highlighted) 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,
|
||||
bool is_highlighted) 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::PlatformCanvasWin* 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 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::PlatformCanvasWin* 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;
|
||||
|
||||
// Gets our singleton instance.
|
||||
static const NativeTheme* instance();
|
||||
|
||||
private:
|
||||
NativeTheme();
|
||||
~NativeTheme();
|
||||
|
||||
HRESULT PaintFrameControl(HDC hdc,
|
||||
RECT* rect,
|
||||
UINT type,
|
||||
UINT state,
|
||||
bool is_highlighted) 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_EVIL_CONSTRUCTORS(NativeTheme);
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
|
||||
#endif // BASE_GFX_NATIVE_THEME_H__
|
@ -1,11 +0,0 @@
|
||||
// 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/gfx/native_theme.h"
|
||||
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
TEST(NativeThemeTest, Init) {
|
||||
ASSERT_TRUE(gfx::NativeTheme::instance() != NULL);
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
// 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 "base/gfx/native_widget_types.h"
|
||||
|
||||
#include "base/gfx/gtk_native_view_id_manager.h"
|
||||
#include "base/logging.h"
|
||||
|
||||
namespace gfx {
|
||||
|
||||
NativeViewId IdFromNativeView(NativeView view) {
|
||||
return Singleton<GtkNativeViewManager>()->GetIdForWidget(view);
|
||||
}
|
||||
|
||||
} // namespace gfx
|
@ -1,7 +0,0 @@
|
||||
// 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.
|
||||
|
||||
// TODO(brettw) this file should be removed and the includes changed to this
|
||||
// new location.
|
||||
#include "skia/ext/platform_canvas.h"
|
@ -1,12 +0,0 @@
|
||||
// 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 BASE_GFX_PLATFORM_CANVAS_LINUX_H_
|
||||
#define BASE_GFX_PLATFORM_CANVAS_LINUX_H_
|
||||
|
||||
// TODO(brettw) this file should be removed and the includes changed to this
|
||||
// new location.
|
||||
#include "skia/ext/platform_canvas_linux.h"
|
||||
|
||||
#endif // BASE_GFX_PLATFORM_CANVAS_LINUX_H_
|
@ -1,12 +0,0 @@
|
||||
// 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 BASE_GFX_PLATFORM_CANVAS_MAC_H__
|
||||
#define BASE_GFX_PLATFORM_CANVAS_MAC_H__
|
||||
|
||||
// TODO(brettw) this file should be removed and the includes changed to this
|
||||
// new location.
|
||||
#include "skia/ext/platform_canvas_mac.h"
|
||||
|
||||
#endif // BASE_GFX_PLATFORM_CANVAS_MAC_H__
|
@ -1,12 +0,0 @@
|
||||
// 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 BASE_GFX_PLATFORM_DEVICE_LINUX_H_
|
||||
#define BASE_GFX_PLATFORM_DEVICE_LINUX_H_
|
||||
|
||||
// TODO(brettw) this file should be removed and the includes changed to this
|
||||
// new location.
|
||||
#include "skia/ext/platform_device_linux.h"
|
||||
|
||||
#endif // BASE_GFX_PLATFORM_DEVICE_LINUX_H_
|
@ -1,12 +0,0 @@
|
||||
// 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 BASE_GFX_PLATFORM_DEVICE_MAC_H__
|
||||
#define BASE_GFX_PLATFORM_DEVICE_MAC_H__
|
||||
|
||||
// TODO(brettw) this file should be removed and the includes changed to this
|
||||
// new location.
|
||||
#include "skia/ext/platform_device_mac.h"
|
||||
|
||||
#endif // BASE_GFX_PLATFORM_DEVICE_MAC_H__
|
@ -1,202 +0,0 @@
|
||||
// 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 "base/gfx/png_encoder.h"
|
||||
#include "base/gfx/png_decoder.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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(PNGEncoder::Encode(&original[0], PNGEncoder::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(PNGDecoder::Decode(&encoded[0], encoded.size(),
|
||||
PNGDecoder::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(PNGEncoder::Encode(&original[0], PNGEncoder::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(PNGDecoder::Decode(&encoded[0], encoded.size(),
|
||||
PNGDecoder::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(PNGDecoder::Decode(&original[0], original.size(),
|
||||
PNGDecoder::FORMAT_RGB, &output,
|
||||
&outw, &outh));
|
||||
|
||||
// Make some compressed data.
|
||||
std::vector<unsigned char> compressed;
|
||||
EXPECT_TRUE(PNGEncoder::Encode(&original[0], PNGEncoder::FORMAT_RGB, w, h,
|
||||
w * 3, false, &compressed));
|
||||
|
||||
// Try decompressing a truncated version.
|
||||
EXPECT_FALSE(PNGDecoder::Decode(&compressed[0], compressed.size() / 2,
|
||||
PNGDecoder::FORMAT_RGB, &output,
|
||||
&outw, &outh));
|
||||
|
||||
// Corrupt it and try decompressing that.
|
||||
for (int i = 10; i < 30; i++)
|
||||
compressed[i] = i;
|
||||
EXPECT_FALSE(PNGDecoder::Decode(&compressed[0], compressed.size(),
|
||||
PNGDecoder::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(PNGEncoder::Encode(&original[0], PNGEncoder::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(PNGDecoder::Decode(&encoded[0], encoded.size(),
|
||||
PNGDecoder::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(PNGEncoder::Encode(&original_rgba[0],
|
||||
PNGEncoder::FORMAT_RGBA,
|
||||
w, h,
|
||||
w * 4, true, &encoded));
|
||||
|
||||
// Decode the RGB to RGBA.
|
||||
std::vector<unsigned char> decoded;
|
||||
int outw, outh;
|
||||
EXPECT_TRUE(PNGDecoder::Decode(&encoded[0], encoded.size(),
|
||||
PNGDecoder::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(PNGEncoder::Encode(&original_rgba[0],
|
||||
PNGEncoder::FORMAT_RGBA,
|
||||
w, h,
|
||||
w * 4, false, &encoded));
|
||||
|
||||
// Decode the RGBA to RGB.
|
||||
EXPECT_TRUE(PNGDecoder::Decode(&encoded[0], encoded.size(),
|
||||
PNGDecoder::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);
|
||||
}
|
@ -1,354 +0,0 @@
|
||||
// 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/gfx/png_decoder.h"
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "skia/include/SkBitmap.h"
|
||||
|
||||
extern "C" {
|
||||
#include "third_party/libpng/png.h"
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// Converts BGRA->RGBA and RGBA->BGRA.
|
||||
void ConvertBetweenBGRAandRGBA(const unsigned char* input, int pixel_width,
|
||||
unsigned char* output) {
|
||||
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) {
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
} // 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;
|
||||
|
||||
// Maximum pixel dimension we'll try to decode.
|
||||
const png_uint_32 kMaxSize = 4096;
|
||||
|
||||
class PngDecoderState {
|
||||
public:
|
||||
PngDecoderState(PNGDecoder::ColorFormat ofmt, std::vector<unsigned char>* o)
|
||||
: output_format(ofmt),
|
||||
output_channels(0),
|
||||
output(o),
|
||||
row_converter(NULL),
|
||||
width(0),
|
||||
height(0),
|
||||
done(false) {
|
||||
}
|
||||
|
||||
PNGDecoder::ColorFormat output_format;
|
||||
int output_channels;
|
||||
|
||||
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);
|
||||
|
||||
// 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_EVIL_CONSTRUCTORS(PngDecoderState);
|
||||
};
|
||||
|
||||
void ConvertRGBtoRGBA(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;
|
||||
}
|
||||
}
|
||||
|
||||
void ConvertRGBtoBGRA(const unsigned char* rgb, int pixel_width,
|
||||
unsigned char* bgra) {
|
||||
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.
|
||||
if (w > kMaxSize || h > kMaxSize)
|
||||
longjmp(png_ptr->jmpbuf, 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 PNGDecoder::FORMAT_RGB:
|
||||
state->row_converter = NULL; // no conversion necessary
|
||||
state->output_channels = 3;
|
||||
break;
|
||||
case PNGDecoder::FORMAT_RGBA:
|
||||
state->row_converter = &ConvertRGBtoRGBA;
|
||||
state->output_channels = 4;
|
||||
break;
|
||||
case PNGDecoder::FORMAT_BGRA:
|
||||
state->row_converter = &ConvertRGBtoBGRA;
|
||||
state->output_channels = 4;
|
||||
break;
|
||||
default:
|
||||
NOTREACHED() << "Unknown output format";
|
||||
break;
|
||||
}
|
||||
} else if (channels == 4) {
|
||||
switch (state->output_format) {
|
||||
case PNGDecoder::FORMAT_RGB:
|
||||
state->row_converter = &ConvertRGBAtoRGB;
|
||||
state->output_channels = 3;
|
||||
break;
|
||||
case PNGDecoder::FORMAT_RGBA:
|
||||
state->row_converter = NULL; // no conversion necessary
|
||||
state->output_channels = 4;
|
||||
break;
|
||||
case PNGDecoder::FORMAT_BGRA:
|
||||
state->row_converter = &ConvertBetweenBGRAandRGBA;
|
||||
state->output_channels = 4;
|
||||
break;
|
||||
default:
|
||||
NOTREACHED() << "Unknown output format";
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
NOTREACHED() << "Unknown input channels";
|
||||
longjmp(png_ptr->jmpbuf, 1);
|
||||
}
|
||||
|
||||
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* dest = &(*state->output)[
|
||||
state->width * state->output_channels * row_num];
|
||||
if (state->row_converter)
|
||||
state->row_converter(new_row, state->width, dest);
|
||||
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_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
bool PNGDecoder::Decode(const unsigned char* input, size_t input_size,
|
||||
ColorFormat format, std::vector<unsigned char>* output,
|
||||
int* w, int* h) {
|
||||
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_struct* png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
|
||||
png_voidp_NULL,
|
||||
png_error_ptr_NULL,
|
||||
png_error_ptr_NULL);
|
||||
if (!png_ptr)
|
||||
return false;
|
||||
|
||||
png_info* info_ptr = png_create_info_struct(png_ptr);
|
||||
if (!info_ptr) {
|
||||
png_destroy_read_struct(&png_ptr, NULL, NULL);
|
||||
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 PNGDecoder::Decode(const std::vector<unsigned char>* data,
|
||||
SkBitmap* bitmap) {
|
||||
DCHECK(bitmap);
|
||||
if (!data || data->empty())
|
||||
return false;
|
||||
int width, height;
|
||||
std::vector<unsigned char> decoded_data;
|
||||
if (PNGDecoder::Decode(&data->front(), data->size(), PNGDecoder::FORMAT_BGRA,
|
||||
&decoded_data, &width, &height)) {
|
||||
bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);
|
||||
bitmap->allocPixels();
|
||||
memcpy(bitmap->getPixels(), &decoded_data.front(), width * height * 4);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//static
|
||||
SkBitmap* PNGDecoder::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;
|
||||
}
|
@ -1,63 +0,0 @@
|
||||
// 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 BASE_GFX_PNG_DECODER_H_
|
||||
#define BASE_GFX_PNG_DECODER_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "base/basictypes.h"
|
||||
|
||||
class SkBitmap;
|
||||
|
||||
// Interface for 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 PNGDecoder {
|
||||
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
|
||||
};
|
||||
|
||||
// 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);
|
||||
|
||||
// A convenience function for decoding PNGs as previously encoded by the PNG
|
||||
// encoder. Chrome encodes png in the format PNGDecoder::FORMAT_BGRA.
|
||||
//
|
||||
// Returns true if data is non-null and can be decoded as a png, false
|
||||
// otherwise.
|
||||
static bool Decode(const std::vector<unsigned char>* data, SkBitmap* icon);
|
||||
|
||||
// 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(PNGDecoder);
|
||||
};
|
||||
|
||||
#endif // BASE_GFX_PNG_DECODER_H_
|
@ -1,205 +0,0 @@
|
||||
// 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 "base/gfx/png_encoder.h"
|
||||
#include "base/logging.h"
|
||||
#include "skia/include/SkBitmap.h"
|
||||
|
||||
extern "C" {
|
||||
#include "third_party/libpng/png.h"
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// Converts BGRA->RGBA and RGBA->BGRA.
|
||||
void ConvertBetweenBGRAandRGBA(const unsigned char* input, int pixel_width,
|
||||
unsigned char* output) {
|
||||
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) {
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// 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 {
|
||||
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 ConvertBGRAtoRGB(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];
|
||||
}
|
||||
}
|
||||
|
||||
// Automatically destroys the given write structs on destruction to make
|
||||
// cleanup and error handling code cleaner.
|
||||
class PngWriteStructDestroyer {
|
||||
public:
|
||||
PngWriteStructDestroyer(png_struct** ps, png_info** pi) : ps_(ps), pi_(pi) {
|
||||
}
|
||||
~PngWriteStructDestroyer() {
|
||||
png_destroy_write_struct(ps_, pi_);
|
||||
}
|
||||
private:
|
||||
png_struct** ps_;
|
||||
png_info** pi_;
|
||||
|
||||
DISALLOW_EVIL_CONSTRUCTORS(PngWriteStructDestroyer);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
bool PNGEncoder::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.
|
||||
void (*converter)(const unsigned char* in, int w, unsigned char* out) = 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;
|
||||
|
||||
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,
|
||||
png_voidp_NULL,
|
||||
png_error_ptr_NULL,
|
||||
png_error_ptr_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;
|
||||
}
|
||||
PngWriteStructDestroyer 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;
|
||||
}
|
||||
|
||||
// Set our callback for libpng to give us the data.
|
||||
PngEncoderState state(output);
|
||||
png_set_write_fn(png_ptr, &state, EncoderWriteCallback, NULL);
|
||||
|
||||
png_set_IHDR(png_ptr, info_ptr, w, h, 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 < h; 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[w * output_color_components];
|
||||
for (int y = 0; y < h; y ++) {
|
||||
converter(&input[y * row_byte_width], w, row);
|
||||
png_write_row(png_ptr, row);
|
||||
}
|
||||
delete[] row;
|
||||
}
|
||||
|
||||
png_write_end(png_ptr, info_ptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
bool PNGEncoder::EncodeBGRASkBitmap(const SkBitmap& input,
|
||||
bool discard_transparency,
|
||||
std::vector<unsigned char>* output) {
|
||||
SkAutoLockPixels input_lock(input);
|
||||
DCHECK(input.empty() || input.bytesPerPixel() == 4);
|
||||
return Encode(static_cast<unsigned char*>(input.getPixels()),
|
||||
PNGEncoder::FORMAT_BGRA, input.width(), input.height(),
|
||||
input.rowBytes(), discard_transparency, output);
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
// 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/gfx/point.h"
|
||||
|
||||
#if defined(OS_WIN)
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
namespace gfx {
|
||||
|
||||
Point::Point() : x_(0), y_(0) {
|
||||
}
|
||||
|
||||
Point::Point(int x, int y) : x_(x), y_(y) {
|
||||
}
|
||||
|
||||
#if defined(OS_WIN)
|
||||
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
|
||||
|
||||
} // namespace gfx
|
@ -1,226 +0,0 @@
|
||||
// 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 "base/gfx/rect.h"
|
||||
|
||||
#if defined(OS_WIN)
|
||||
#include <windows.h>
|
||||
#elif defined(OS_MACOSX)
|
||||
#include <CoreGraphics/CGGeometry.h>
|
||||
#elif defined(OS_LINUX)
|
||||
#include <gdk/gdk.h>
|
||||
#endif
|
||||
|
||||
#include "base/logging.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) {
|
||||
set_width(width);
|
||||
set_height(height);
|
||||
}
|
||||
|
||||
Rect::Rect(int x, int y, int width, int height)
|
||||
: origin_(x, y) {
|
||||
set_width(width);
|
||||
set_height(height);
|
||||
}
|
||||
|
||||
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_LINUX)
|
||||
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::set_width(int width) {
|
||||
size_.set_width(width);
|
||||
}
|
||||
void Rect::set_height(int height) {
|
||||
size_.set_height(height);
|
||||
}
|
||||
|
||||
void Rect::SetRect(int x, int y, int width, int height) {
|
||||
origin_.SetPoint(x, y);
|
||||
set_width(width);
|
||||
set_height(height);
|
||||
}
|
||||
|
||||
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) {
|
||||
set_x(x() + horizontal);
|
||||
set_y(y() + vertical);
|
||||
}
|
||||
|
||||
bool Rect::operator==(const Rect& other) const {
|
||||
return origin_ == other.origin_ && size_ == other.size_;
|
||||
}
|
||||
|
||||
#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_LINUX)
|
||||
GdkRectangle Rect::ToGdkRectangle() const {
|
||||
GdkRectangle r = {x(), y(), width(), height()};
|
||||
return r;
|
||||
}
|
||||
#elif defined(OS_MACOSX)
|
||||
CGRect Rect::ToCGRect() const {
|
||||
return CGRectMake(x(), y(), width(), height());
|
||||
}
|
||||
#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);
|
||||
}
|
||||
|
||||
} // namespace gfx
|
@ -1,162 +0,0 @@
|
||||
// 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.
|
||||
|
||||
// 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 contain()) to complain in this case.
|
||||
|
||||
#ifndef BASE_GFX_RECT_H__
|
||||
#define BASE_GFX_RECT_H__
|
||||
|
||||
#include <ostream>
|
||||
|
||||
#include "base/gfx/point.h"
|
||||
#include "base/gfx/size.h"
|
||||
|
||||
#if defined(OS_WIN)
|
||||
typedef struct tagRECT RECT;
|
||||
#elif defined(OS_LINUX)
|
||||
typedef struct _GdkRectangle GdkRectangle;
|
||||
#endif
|
||||
|
||||
namespace gfx {
|
||||
|
||||
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(OS_LINUX)
|
||||
explicit Rect(const GdkRectangle& r);
|
||||
#endif
|
||||
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(OS_LINUX)
|
||||
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);
|
||||
|
||||
int height() const { return size_.height(); }
|
||||
void set_height(int height);
|
||||
|
||||
const gfx::Point& origin() const { return origin_; }
|
||||
void set_origin(const gfx::Point& origin) { origin_ = origin; }
|
||||
|
||||
const gfx::Size& size() const { return 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 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);
|
||||
}
|
||||
|
||||
#if defined(OS_WIN)
|
||||
// Construct an equivalent Win32 RECT object.
|
||||
RECT ToRECT() const;
|
||||
#elif defined(OS_LINUX)
|
||||
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;
|
||||
|
||||
private:
|
||||
gfx::Point origin_;
|
||||
gfx::Size size_;
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& out, const gfx::Rect& r) {
|
||||
return out << r.origin() << " " << r.size();
|
||||
}
|
||||
|
||||
#endif // BASE_GFX_RECT_H__
|
@ -1,280 +0,0 @@
|
||||
// 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 "base/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());
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
// 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/gfx/size.h"
|
||||
|
||||
#if defined(OS_WIN)
|
||||
#include <windows.h>
|
||||
#elif defined(OS_MACOSX)
|
||||
#include <CoreGraphics/CGGeometry.h>
|
||||
#endif
|
||||
|
||||
#include "base/logging.h"
|
||||
|
||||
|
||||
namespace gfx {
|
||||
|
||||
Size::Size(int width, int height) {
|
||||
set_width(width);
|
||||
set_height(height);
|
||||
}
|
||||
|
||||
#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();
|
||||
width = 0;
|
||||
}
|
||||
width_ = width;
|
||||
}
|
||||
|
||||
void Size::set_height(int height) {
|
||||
if (height < 0) {
|
||||
NOTREACHED();
|
||||
height = 0;
|
||||
}
|
||||
height_ = height;
|
||||
}
|
||||
|
||||
|
||||
} // namespace gfx
|
@ -1,76 +0,0 @@
|
||||
// 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 BASE_GFX_SIZE_H__
|
||||
#define BASE_GFX_SIZE_H__
|
||||
|
||||
#include "build/build_config.h"
|
||||
|
||||
#include <ostream>
|
||||
|
||||
#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);
|
||||
|
||||
~Size() {}
|
||||
|
||||
int width() const { return width_; }
|
||||
int height() const { return 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_;
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& out, const gfx::Size& s) {
|
||||
return out << s.width() << "x" << s.height();
|
||||
}
|
||||
|
||||
#endif // BASE_GFX_SIZE_H__
|
Loading…
Reference in New Issue
Block a user