Implement non-rectangular windows using the Window Driver mechanism.

git-svn-id: file:///fltk/svn/fltk/branches/branch-1.3-porting@11336 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
This commit is contained in:
Manolo Gouy 2016-03-10 17:19:34 +00:00
parent 79f79d292c
commit d4768073fa
19 changed files with 627 additions and 129 deletions

View File

@ -41,6 +41,7 @@ class Fl_Overlay_Window;
class FL_EXPORT Fl_Double_Window : public Fl_Window {
protected:
void flush(int eraseoverlay);
Fl_Double_Window(int X, int Y, int W, int H, const char *l, Fl_Window_Driver *driver);
public:
/**
Return non-null if this is an Fl_Overlay_Window object.

View File

@ -101,42 +101,6 @@ class FL_EXPORT Fl_Window : public Fl_Group {
// cursor stuff
Fl_Cursor cursor_default;
protected:
/** Data supporting a non-rectangular window shape */
struct shape_data_type {
int lw_; ///< width of shape image
int lh_; ///< height of shape image
Fl_Image* shape_; ///< shape image
#if defined(__APPLE__) // PORTME: Fl_Window_Driver - per-window shape information, move to Fl_X/Fl_Window_Driver
typedef struct CGImage* CGImageRef;
CGImageRef mask;
#elif defined(WIN32)
// not needed
#elif defined(FL_PORTING)
# pragma message "FL_PORTING: define storage for a window mask here if needed"
#else // X11
// not needed
#endif
Fl_Bitmap *todelete_; ///< auxiliary bitmap image
};
shape_data_type *shape_data_; ///< non-null means the window has a non-rectangular shape
private:
void shape_bitmap_(Fl_Image* b);
void shape_alpha_(Fl_Image* img, int offset);
void shape_pixmap_(Fl_Image* pixmap);
public:
void shape(const Fl_Image* img);
/** Set the window's shape with an Fl_Image.
\see void shape(const Fl_Image* img)
*/
inline void shape(const Fl_Image& b) { shape(&b); }
#if defined(WIN32) || defined(__APPLE__) || defined(FL_DOXYGEN) // PORTME: Fl_Window_Driver - per-window shape
#elif defined(FL_PORTING)
# pragma message "FL_PORTING: do you need a combine_mask() function in Fl_Window?"
#else // X11
void combine_mask(void);
#endif
private:
void size_range_();
@ -152,7 +116,7 @@ protected:
/** Stores the last window that was made current. See current() const */
static Fl_Window *current_;
virtual void draw();
void draw();
/** Forces the window to be drawn, this window is also made current and calls draw(). */
virtual void flush();
@ -180,6 +144,7 @@ protected:
void free_icons();
Fl_Window(int x, int y, int w, int h, const char* title, Fl_Window_Driver *driver);
public:
/**
@ -640,9 +605,13 @@ public:
Fl_Window_Driver *driver() { return pWindowDriver; }
/**
Return non-null if this is an Fl_Overlay_Window object.
Return non-null if this is an Fl_Double_Window object.
*/
virtual Fl_Double_Window *as_double_window() {return NULL; }
virtual Fl_Double_Window *as_double_window() {return NULL;}
void shape(const Fl_Image* img);
void shape(const Fl_Image& b) ;
int is_shaped();
};
#endif

View File

@ -28,14 +28,18 @@
class Fl_Window;
class Fl_Image;
/**
\brief A base class for platform specific window handling code.
*/
class FL_EXPORT Fl_Window_Driver {
friend class Fl_Window;
protected:
Fl_Window *pWindow;
struct shape_data_type;
shape_data_type *shape_data_; ///< non-null means the window has a non-rectangular shape
public:
Fl_Window_Driver(Fl_Window *);
virtual ~Fl_Window_Driver();
@ -47,7 +51,10 @@ public:
virtual void take_focus() { }
virtual int double_flush(int eraseoverlay);
virtual void destroy_double_buffer();
void draw();
virtual void draw();
void shape_pixmap_(Fl_Image* pixmap);
virtual void shape(const Fl_Image* img) {}
virtual void shape_alpha_(Fl_Image* img, int offset) {}
};

View File

@ -755,8 +755,7 @@ inline void fl_draw_image_mono(Fl_Draw_Image_Cb cb, void* data, int X,int Y,int
\returns 1 if true alpha blending supported by platform
\returns 0 not supported so FLTK will use screen door transparency
*/
/* note: doxygen comment here to avoid triplication in os-speciic files */
FL_EXPORT char fl_can_do_alpha_blending();
inline char fl_can_do_alpha_blending() {return Fl_Display_Device::display_device()->driver()->can_do_alpha_blending();}
/**
Reads an RGB(A) image from the current window or off-screen buffer.

View File

@ -84,7 +84,6 @@ set(CPPFILES
Fl_Window_fullscreen.cxx
Fl_Window_hotspot.cxx
Fl_Window_iconize.cxx
Fl_Window_shape.cxx
Fl_Wizard.cxx
Fl_XBM_Image.cxx
Fl_XPM_Image.cxx

View File

@ -21,7 +21,6 @@
#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Overlay_Window.H>
#include <FL/Fl_Printer.H>
#include <FL/x.H>
#include <FL/fl_draw.H>
@ -33,14 +32,20 @@
Fl_Double_Window::Fl_Double_Window(int W, int H, const char *l)
: Fl_Window(W,H,l)
: Fl_Window(0, 0, W, H, l, Fl_Window_Driver::newWindowDriver(this))
{
type(FL_DOUBLE_WINDOW);
clear_flag(FORCE_POSITION);
}
Fl_Double_Window::Fl_Double_Window(int X, int Y, int W, int H, const char *l)
: Fl_Window(X,Y,W,H,l)
: Fl_Window(X,Y,W,H,l, Fl_Window_Driver::newWindowDriver(this))
{
type(FL_DOUBLE_WINDOW);
}
Fl_Double_Window::Fl_Double_Window(int x, int y, int w, int h, const char* title, Fl_Window_Driver *driver) : Fl_Window(x,y,w,h,title, driver)
{
type(FL_DOUBLE_WINDOW);
}
@ -51,19 +56,9 @@ void Fl_Double_Window::show() {
}
/** see fl_copy_offscreen() */
void Fl_Graphics_Driver::copy_offscreen(int x, int y, int w, int h, Fl_Offscreen pixmap, int srcx, int srcy)
{
fl_begin_offscreen(pixmap);
uchar *img = fl_read_image(NULL, srcx, srcy, w, h, 0);
fl_end_offscreen();
fl_draw_image(img, x, y, w, h, 3, 0);
delete[] img;
}
char fl_can_do_alpha_blending() {
/*char fl_can_do_alpha_blending() {
return Fl_Display_Device::display_device()->driver()->can_do_alpha_blending();
}
}*/
/**
Forces the window to be redrawn.
@ -94,29 +89,6 @@ void Fl_Double_Window::flush(int eraseoverlay) {
}
}
int Fl_Window_Driver::double_flush(int eraseoverlay) {
/* This is a working, platform-independent implementation.
Some platforms may re-implement it for their own logic:
- on Mac OS, the system double buffers all windows, so it is
reimplemented to do the same as Fl_Window::flush(), except for
Fl_Overlay_Window's which fall back on this implementation.
- on Xlib, it is reimplemented if the Xdbe extension is available.
*/
Fl_X *i = Fl_X::i(pWindow);
if (!i->other_xid) {
i->other_xid = fl_create_offscreen(pWindow->w(), pWindow->h());
pWindow->clear_damage(FL_DAMAGE_ALL);
}
if (pWindow->damage() & ~FL_DAMAGE_EXPOSE) {
fl_clip_region(i->region); i->region = 0;
fl_begin_offscreen(i->other_xid);
fl_graphics_driver->clip_region( 0 );
draw();
fl_end_offscreen();
}
return 0;
}
void Fl_Double_Window::resize(int X,int Y,int W,int H) {
int ow = w();
@ -135,16 +107,6 @@ void Fl_Double_Window::hide() {
Fl_Window::hide();
}
void Fl_Window_Driver::destroy_double_buffer() {
Fl_X *i = Fl_X::i(pWindow);
/* This is a working, platform-independent implementation.
Some platforms may re-implement it for their own logic:
- on Xlib, it is reimplemented if the Xdbe extension is available.
*/
fl_delete_offscreen(i->other_xid);
i->other_xid = 0;
}
/**
The destructor <I>also deletes all the children</I>. This allows a
@ -156,20 +118,6 @@ Fl_Double_Window::~Fl_Double_Window() {
}
Fl_Overlay_Window::Fl_Overlay_Window(int W, int H, const char *l)
: Fl_Double_Window(W,H,l)
{
overlay_ = 0;
image(0);
}
Fl_Overlay_Window::Fl_Overlay_Window(int X, int Y, int W, int H, const char *l)
: Fl_Double_Window(X,Y,W,H,l)
{
overlay_ = 0;
image(0);
}
//

View File

@ -61,6 +61,16 @@ int Fl_Graphics_Driver::draw_scaled(Fl_Image *img, int X, int Y, int W, int H) {
return 0;
}
/** see fl_copy_offscreen() */
void Fl_Graphics_Driver::copy_offscreen(int x, int y, int w, int h, Fl_Offscreen pixmap, int srcx, int srcy)
{
fl_begin_offscreen(pixmap);
uchar *img = fl_read_image(NULL, srcx, srcy, w, h, 0);
fl_end_offscreen();
fl_draw_image(img, x, y, w, h, 3, 0);
delete[] img;
}
//
// End of "$Id$".

View File

@ -26,6 +26,23 @@
#include <FL/Fl_Overlay_Window.H>
#include <FL/fl_draw.H>
#include <FL/x.H>
#include <FL/Fl_Window_Driver.H>
Fl_Overlay_Window::Fl_Overlay_Window(int W, int H, const char *l)
: Fl_Double_Window(0,0,W,H,l,Fl_Window_Driver::newWindowDriver(this))
{
overlay_ = 0;
image(0);
clear_flag(FORCE_POSITION);
}
Fl_Overlay_Window::Fl_Overlay_Window(int X, int Y, int W, int H, const char *l)
: Fl_Double_Window(X,Y,W,H,l,Fl_Window_Driver::newWindowDriver(this))
{
overlay_ = 0;
image(0);
}
void Fl_Overlay_Window::show() {
Fl_Double_Window::show();

View File

@ -35,6 +35,7 @@
char *Fl_Window::default_xclass_ = 0L;
void Fl_Window::_Fl_Window() {
cursor_default = FL_CURSOR_DEFAULT;
type(FL_WINDOW);
box(FL_FLAT_BOX);
if (Fl::scheme_bg_) {
@ -52,7 +53,6 @@ void Fl_Window::_Fl_Window() {
resizable(0);
size_range_set = 0;
minw = maxw = minh = maxh = 0;
shape_data_ = NULL;
no_fullscreen_x = 0;
no_fullscreen_y = 0;
no_fullscreen_w = w();
@ -68,19 +68,22 @@ Fl_Window::Fl_Window(int X,int Y,int W, int H, const char *l) :
Fl_Group(X, Y, W, H, l),
pWindowDriver(Fl_Window_Driver::newWindowDriver(this))
{
cursor_default = FL_CURSOR_DEFAULT;
_Fl_Window();
set_flag(FORCE_POSITION);
}
Fl_Window::Fl_Window(int X,int Y,int W, int H, const char *l, Fl_Window_Driver *driver) : Fl_Group(X, Y, W, H, l), pWindowDriver(driver)
{
_Fl_Window();
set_flag(FORCE_POSITION);
}
Fl_Window::Fl_Window(int W, int H, const char *l) :
// fix common user error of a missing end() with current(0):
Fl_Group((Fl_Group::current(0),0), 0, W, H, l),
pWindowDriver(Fl_Window_Driver::newWindowDriver(this))
{
cursor_default = FL_CURSOR_DEFAULT;
_Fl_Window();
clear_visible();
}
@ -92,15 +95,6 @@ Fl_Window::~Fl_Window() {
}
free_icons();
delete icon_;
if (shape_data_) {
if (shape_data_->todelete_) delete shape_data_->todelete_;
#if defined(__APPLE__) // PORTME: Fl_Window_Driver - platform window driver
if (shape_data_->mask) {
CGImageRelease(shape_data_->mask);
}
#endif
delete shape_data_;
}
}

View File

@ -22,11 +22,13 @@
#include "config_lib.h"
#include <FL/Fl_Window_Driver.H>
#include <FL/Fl.H>
#include <FL/fl_draw.H>
Fl_Window_Driver::Fl_Window_Driver(Fl_Window *win) :
pWindow(win)
{
shape_data_ = NULL;
}
@ -34,10 +36,129 @@ Fl_Window_Driver::~Fl_Window_Driver()
{
}
void Fl_Window_Driver::draw() {
pWindow->draw();
int Fl_Window_Driver::double_flush(int eraseoverlay) {
/* This is a working, platform-independent implementation.
Some platforms may re-implement it for their own logic:
- on Mac OS, the system double buffers all windows, so it is
reimplemented to do the same as Fl_Window::flush(), except for
Fl_Overlay_Window's which fall back on this implementation.
- on Xlib, it is reimplemented if the Xdbe extension is available.
*/
Fl_X *i = Fl_X::i(pWindow);
if (!i->other_xid) {
i->other_xid = fl_create_offscreen(pWindow->w(), pWindow->h());
pWindow->clear_damage(FL_DAMAGE_ALL);
}
if (pWindow->damage() & ~FL_DAMAGE_EXPOSE) {
fl_clip_region(i->region); i->region = 0;
fl_begin_offscreen(i->other_xid);
fl_graphics_driver->clip_region( 0 );
draw();
fl_end_offscreen();
}
return 0;
}
void Fl_Window_Driver::destroy_double_buffer() {
Fl_X *i = Fl_X::i(pWindow);
/* This is a working, platform-independent implementation.
Some platforms may re-implement it for their own logic:
- on Xlib, it is reimplemented if the Xdbe extension is available.
*/
fl_delete_offscreen(i->other_xid);
i->other_xid = 0;
}
void Fl_Window_Driver::draw() {
// The following is similar to Fl_Group::draw(), but ...
//
// - draws the box at (0,0), i.e. with x=0 and y=0 instead of x() and y()
// - does NOT draw the label (text)
// - draws the image only if FL_ALIGN_INSIDE is set
//
// Note: The label (text) of top level windows is drawn in the title bar.
// Other windows do not draw their labels at all, unless drawn by their
// parent widgets or by special draw() methods (derived classes).
if (pWindow->damage() & ~FL_DAMAGE_CHILD) { // draw the entire thing
pWindow->draw_box(pWindow->box(),0,0,pWindow->w(),pWindow->h(),pWindow->color()); // draw box with x/y = 0
if (pWindow->image() && (pWindow->align() & FL_ALIGN_INSIDE)) { // draw the image only
Fl_Label l1;
memset(&l1,0,sizeof(l1));
l1.align_ = pWindow->align();
l1.image = pWindow->image();
if (!pWindow->active_r() && l1.image && l1.deimage) l1.image = l1.deimage;
l1.type = pWindow->labeltype();
l1.draw(0,0,pWindow->w(),pWindow->h(),pWindow->align());
}
}
pWindow->draw_children();
# if defined(FLTK_USE_CAIRO)
Fl::cairo_make_current(this); // checkout if an update is necessary
# endif
}
void Fl_Window::draw() {pWindowDriver->draw();}
/** Assigns a non-rectangular shape to the window.
This function gives an arbitrary shape (not just a rectangular region) to an Fl_Window.
An Fl_Image of any dimension can be used as mask; it is rescaled to the window's dimension as needed.
The layout and widgets inside are unaware of the mask shape, and most will act as though the window's
rectangular bounding box is available
to them. It is up to you to make sure they adhere to the bounds of their masking shape.
The \p img argument can be an Fl_Bitmap, Fl_Pixmap, Fl_RGB_Image or Fl_Shared_Image:
\li With Fl_Bitmap or Fl_Pixmap, the shaped window covers the image part where bitmap bits equal one,
or where the pixmap is not fully transparent.
\li With an Fl_RGB_Image with an alpha channel (depths 2 or 4), the shaped window covers the image part
that is not fully transparent.
\li With an Fl_RGB_Image of depth 1 (gray-scale) or 3 (RGB), the shaped window covers the non-black image part.
\li With an Fl_Shared_Image, the shape is determined by rules above applied to the underlying image.
The shared image should not have been scaled through Fl_Shared_Image::scale().
Platform details:
\li On the unix/linux platform, the SHAPE extension of the X server is required.
This function does control the shape of Fl_Gl_Window instances.
\li On the MSWindows platform, this function does nothing with class Fl_Gl_Window.
\li On the Mac platform, OS version 10.4 or above is required.
An 8-bit shape-mask is used when \p img is an Fl_RGB_Image:
with depths 2 or 4, the image alpha channel becomes the shape mask such that areas with alpha = 0
are out of the shaped window;
with depths 1 or 3, white and black are in and out of the
shaped window, respectively, and other colors give intermediate masking scores.
This function does nothing with class Fl_Gl_Window.
The window borders and caption created by the window system are turned off by default. They
can be re-enabled by calling Fl_Window::border(1).
A usage example is found at example/shapedwindow.cxx.
\version 1.3.3 (and requires compilation with FL_ABI_VERSION >= 10303)
*/
void Fl_Window::shape(const Fl_Image* img) {pWindowDriver->shape(img);}
/** Set the window's shape with an Fl_Image.
\see void shape(const Fl_Image* img)
*/
void Fl_Window::shape(const Fl_Image& img) {pWindowDriver->shape(&img);}
/** Returns non NULL when the window has been assigned a non-rectangular shape */
int Fl_Window::is_shaped() {return pWindowDriver->shape_data_ != NULL;}
void Fl_Window_Driver::shape_pixmap_(Fl_Image* pixmap) {
Fl_RGB_Image* rgba = new Fl_RGB_Image((Fl_Pixmap*)pixmap);
shape_alpha_(rgba, 3);
delete rgba;
}
//
// End of "$Id$".
//

View File

@ -2998,7 +2998,7 @@ void Fl_X::make(Fl_Window* w)
[cw setHasShadow:YES];
[cw setAcceptsMouseMovedEvents:YES];
}
if (w->shape_data_) {
if (w->is_shaped()) {
[cw setOpaque:NO]; // shaped windows must be non opaque
[cw setBackgroundColor:[NSColor clearColor]]; // and with transparent background color
}

View File

@ -53,6 +53,7 @@ class Fl_Widget *fl_selection_requestor;
# include <stdlib.h>
# include "flstring.h"
# include "drivers/X11/Fl_X11_Screen_Driver.H"
# include "drivers/X11/Fl_X11_Window_Driver.H"
# include <unistd.h>
# include <time.h>
# include <sys/time.h>
@ -2557,8 +2558,8 @@ void Fl_X::make_xid(Fl_Window* win, XVisualInfo *visual, Colormap colormap)
}
#endif
if (win->shape_data_) {
win->combine_mask();
if (win->is_shaped()) {
((Fl_X11_Window_Driver*)win->pWindowDriver)->combine_mask();
}
XMapWindow(fl_display, xp->xid);
if (showit) {

View File

@ -103,7 +103,6 @@ CPPFILES = \
Fl_Window_fullscreen.cxx \
Fl_Window_hotspot.cxx \
Fl_Window_iconize.cxx \
Fl_Window_shape.cxx \
Fl_Wizard.cxx \
Fl_XBM_Image.cxx \
Fl_XPM_Image.cxx \

View File

@ -26,7 +26,10 @@
#define FL_COCOA_WINDOW_DRIVER_H
#include <FL/Fl_Window_Driver.H>
#include <ApplicationServices/ApplicationServices.h>
class Fl_Image;
class Fl_Window;
/*
Move everything here that manages the native window interface.
@ -41,12 +44,26 @@
? where do we handle the interface between OpenGL/DirectX and Cocoa/WIN32/Glx?
*/
struct Fl_Window_Driver::shape_data_type {
int lw_; ///< width of shape image
int lh_; ///< height of shape image
Fl_Image* shape_; ///< shape image
typedef struct CGImage* CGImageRef;
CGImageRef mask;
Fl_Bitmap *todelete_; ///< auxiliary bitmap image
};
class FL_EXPORT Fl_Cocoa_Window_Driver : public Fl_Window_Driver
{
private:
void shape_bitmap_(Fl_Image* b);
void shape_alpha_(Fl_Image* img, int offset);
public:
Fl_Cocoa_Window_Driver(Fl_Window*);
~Fl_Cocoa_Window_Driver();
virtual void take_focus();
virtual void shape(const Fl_Image* img);
virtual void draw();
};

View File

@ -21,6 +21,7 @@
#include "Fl_Cocoa_Window_Driver.h"
#include <FL/Fl_Double_Window.H>
#include <FL/fl_draw.H>
#include <FL/Fl.H>
// class used for Fl_Double_Window but not for Fl_Overlay_Window
class Fl_Cocoa_Double_Window_Driver : public Fl_Cocoa_Window_Driver {
@ -46,6 +47,16 @@ Fl_Cocoa_Window_Driver::Fl_Cocoa_Window_Driver(Fl_Window *win)
{
}
Fl_Cocoa_Window_Driver::~Fl_Cocoa_Window_Driver()
{
if (shape_data_) {
if (shape_data_->todelete_) delete shape_data_->todelete_;
if (shape_data_->mask) {
CGImageRelease(shape_data_->mask);
}
delete shape_data_;
}
}
extern Fl_Window *fl_xfocus;
@ -56,6 +67,129 @@ void Fl_Cocoa_Window_Driver::take_focus()
if (x) x->set_key_window();
}
static void MyProviderReleaseData (void *info, const void *data, size_t size) {
delete[] (uchar*)data;
}
// bitwise inversion of all 4-bit quantities
static const unsigned char swapped[16] = {0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,15};
static inline uchar swap_byte(const uchar b) {
// reverse the order of bits of byte b: 1->8 becomes 8->1
return (swapped[b & 0xF] << 4) | swapped[b >> 4];
}
void Fl_Cocoa_Window_Driver::shape_bitmap_(Fl_Image* b) {
shape_data_->shape_ = b;
if (b) {
// complement mask bits and perform bitwise inversion of all bytes and also reverse top and bottom
int bytes_per_row = (b->w() + 7)/8;
uchar *from = new uchar[bytes_per_row * b->h()];
for (int i = 0; i < b->h(); i++) {
uchar *p = (uchar*)(*b->data()) + bytes_per_row * i;
uchar *last = p + bytes_per_row;
uchar *q = from + (b->h() - 1 - i) * bytes_per_row;
while (p < last) {
*q++ = swap_byte(~*p++);
}
}
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, from, bytes_per_row * b->h(), MyProviderReleaseData);
shape_data_->mask = CGImageMaskCreate(b->w(), b->h(), 1, 1, bytes_per_row, provider, NULL, false);
CFRelease(provider);
}
}
void Fl_Cocoa_Window_Driver::shape_alpha_(Fl_Image* img, int offset) {
int i, d = img->d(), w = img->w(), h = img->h();
shape_data_->shape_ = img;
if (shape_data_->shape_) {
// reverse top and bottom and convert to gray scale if img->d() == 3 and complement bits
int bytes_per_row = w * d;
uchar *from = new uchar[w * h];
for ( i = 0; i < h; i++) {
uchar *p = (uchar*)(*img->data()) + bytes_per_row * i + offset;
uchar *last = p + bytes_per_row;
uchar *q = from + (h - 1 - i) * w;
while (p < last) {
if (d == 3) {
unsigned u = *p++;
u += *p++;
u += *p++;
*q++ = ~(u/3);
}
else {
*q++ = ~(*p);
p += d;
}
}
}
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, from, w * h, MyProviderReleaseData);
shape_data_->mask = CGImageMaskCreate(w, h, 8, 8, w, provider, NULL, false);
CFRelease(provider);
}
}
void Fl_Cocoa_Window_Driver::shape(const Fl_Image* img) {
# if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
if (shape_data_) {
if (shape_data_->todelete_) { delete shape_data_->todelete_; }
if (shape_data_->mask) { CGImageRelease(shape_data_->mask); }
}
else {
shape_data_ = new shape_data_type;
}
memset(shape_data_, 0, sizeof(shape_data_type));
pWindow->border(false);
int d = img->d();
if (d && img->count() >= 2) shape_pixmap_((Fl_Image*)img);
else if (d == 0) shape_bitmap_((Fl_Image*)img);
else if (d == 2 || d == 4) shape_alpha_((Fl_Image*)img, d - 1);
else if ((d == 1 || d == 3) && img->count() == 1) shape_alpha_((Fl_Image*)img, 0);
#endif
}
void Fl_Cocoa_Window_Driver::draw()
{
CGContextRef gc = (CGContextRef)Fl_Surface_Device::surface()->driver()->gc();
if (shape_data_) {
# if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
if (shape_data_->mask && (&CGContextClipToMask != NULL)) {
CGContextClipToMask(gc, CGRectMake(0,0, pWindow->w(), pWindow->h()), shape_data_->mask); // requires Mac OS 10.4
}
CGContextSaveGState(gc);
# endif
}
Fl_Window_Driver::draw();
// on OS X, windows have no frame. Before OS X 10.7, to resize a window, we drag the lower right
// corner. This code draws a little ribbed triangle for dragging.
if (fl_mac_os_version < 100700 && gc && !pWindow->parent() && pWindow->resizable() ) {
int dx = Fl::box_dw(pWindow->box())-Fl::box_dx(pWindow->box());
int dy = Fl::box_dh(pWindow->box())-Fl::box_dy(pWindow->box());
if (dx<=0) dx = 1;
if (dy<=0) dy = 1;
int x1 = pWindow->w()-dx-1, x2 = x1, y1 = pWindow->h()-dx-1, y2 = y1;
Fl_Color c[4] = {
pWindow->color(),
fl_color_average(pWindow->color(), FL_WHITE, 0.7f),
fl_color_average(pWindow->color(), FL_BLACK, 0.6f),
fl_color_average(pWindow->color(), FL_BLACK, 0.8f),
};
int i;
for (i=dx; i<12; i++) {
fl_color(c[i&3]);
fl_line(x1--, y1, x2, y2--);
}
}
# if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
if (shape_data_) CGContextRestoreGState(gc);
# endif
}
//
// End of "$Id$".
//

View File

@ -42,10 +42,22 @@
? where do we handle the interface between OpenGL/DirectX and Cocoa/WIN32/Glx?
*/
struct Fl_Window_Driver::shape_data_type {
int lw_; ///< width of shape image
int lh_; ///< height of shape image
Fl_Image* shape_; ///< shape image
Fl_Bitmap *todelete_; ///< auxiliary bitmap image
};
class FL_EXPORT Fl_WinAPI_Window_Driver : public Fl_Window_Driver
{
private:
void shape_bitmap_(Fl_Image* b);
void shape_alpha_(Fl_Image* img, int offset);
public:
Fl_WinAPI_Window_Driver(Fl_Window*);
virtual void shape(const Fl_Image* img);
virtual void draw();
};

View File

@ -19,7 +19,7 @@
#include "../../config_lib.h"
#include "Fl_WinAPI_Window_Driver.H"
#include <windows.h>
Fl_Window_Driver *Fl_Window_Driver::newWindowDriver(Fl_Window *w)
{
@ -32,6 +32,155 @@ Fl_WinAPI_Window_Driver::Fl_WinAPI_Window_Driver(Fl_Window *win)
{
}
void Fl_WinAPI_Window_Driver::shape_bitmap_(Fl_Image* b) {
shape_data_->shape_ = b;
}
void Fl_WinAPI_Window_Driver::shape_alpha_(Fl_Image* img, int offset) {
int i, j, d = img->d(), w = img->w(), h = img->h(), bytesperrow = (w+7)/8;
unsigned u;
uchar byte, onebit;
// build an Fl_Bitmap covering the non-fully transparent/black part of the image
const uchar* bits = new uchar[h*bytesperrow]; // to store the bitmap
const uchar* alpha = (const uchar*)*img->data() + offset; // points to alpha value of rgba pixels
for (i = 0; i < h; i++) {
uchar *p = (uchar*)bits + i * bytesperrow;
byte = 0;
onebit = 1;
for (j = 0; j < w; j++) {
if (d == 3) {
u = *alpha;
u += *(alpha+1);
u += *(alpha+2);
}
else u = *alpha;
if (u > 0) { // if the pixel is not fully transparent/black
byte |= onebit; // turn on the corresponding bit of the bitmap
}
onebit = onebit << 1; // move the single set bit one position to the left
if (onebit == 0 || j == w-1) {
onebit = 1;
*p++ = byte; // store in bitmap one pack of bits
byte = 0;
}
alpha += d; // point to alpha value of next pixel
}
}
Fl_Bitmap* bitmap = new Fl_Bitmap(bits, w, h);
bitmap->alloc_array = 1;
shape_bitmap_(bitmap);
shape_data_->todelete_ = bitmap;
}
void Fl_WinAPI_Window_Driver::shape(const Fl_Image* img) {
if (shape_data_) {
if (shape_data_->todelete_) { delete shape_data_->todelete_; }
}
else {
shape_data_ = new shape_data_type;
}
memset(shape_data_, 0, sizeof(shape_data_type));
pWindow->border(false);
int d = img->d();
if (d && img->count() >= 2) shape_pixmap_((Fl_Image*)img);
else if (d == 0) shape_bitmap_((Fl_Image*)img);
else if (d == 2 || d == 4) shape_alpha_((Fl_Image*)img, d - 1);
else if ((d == 1 || d == 3) && img->count() == 1) shape_alpha_((Fl_Image*)img, 0);
}
static inline BYTE bit(int x) { return (BYTE)(1 << (x%8)); }
static HRGN bitmap2region(Fl_Image* image) {
HRGN hRgn = 0;
/* Does this need to be dynamically determined, perhaps? */
const int ALLOC_UNIT = 100;
DWORD maxRects = ALLOC_UNIT;
RGNDATA* pData = (RGNDATA*)malloc(sizeof(RGNDATAHEADER)+(sizeof(RECT)*maxRects));
pData->rdh.dwSize = sizeof(RGNDATAHEADER);
pData->rdh.iType = RDH_RECTANGLES;
pData->rdh.nCount = pData->rdh.nRgnSize = 0;
SetRect(&pData->rdh.rcBound, MAXLONG, MAXLONG, 0, 0);
const int bytesPerLine = (image->w() + 7)/8;
BYTE* p, *data = (BYTE*)*image->data();
for (int y = 0; y < image->h(); y++) {
// each row, left to right
for (int x = 0; x < image->w(); x++) {
int x0 = x;
while (x < image->w()) {
p = data + x / 8;
if (!((*p) & bit(x))) break; // transparent pixel
x++;
}
if (x > x0) {
RECT *pr;
/* Add the pixels (x0, y) to (x, y+1) as a new rectangle
* in the region
*/
if (pData->rdh.nCount >= maxRects) {
maxRects += ALLOC_UNIT;
pData = (RGNDATA*)realloc(pData, sizeof(RGNDATAHEADER)
+ (sizeof(RECT)*maxRects));
}
pr = (RECT*)&pData->Buffer;
SetRect(&pr[pData->rdh.nCount], x0, y, x, y+1);
if (x0 < pData->rdh.rcBound.left)
pData->rdh.rcBound.left = x0;
if (y < pData->rdh.rcBound.top)
pData->rdh.rcBound.top = y;
if (x > pData->rdh.rcBound.right)
pData->rdh.rcBound.right = x;
if (y+1 > pData->rdh.rcBound.bottom)
pData->rdh.rcBound.bottom = y+1;
pData->rdh.nCount++;
/* On Windows98, ExtCreateRegion() may fail if the
* number of rectangles is too large (ie: >
* 4000). Therefore, we have to create the region by
* multiple steps.
*/
if (pData->rdh.nCount == 2000) {
HRGN h = ExtCreateRegion(NULL, sizeof(RGNDATAHEADER)
+ (sizeof(RECT)*maxRects), pData);
if (hRgn) {
CombineRgn(hRgn, hRgn, h, RGN_OR);
DeleteObject(h);
} else
hRgn = h;
pData->rdh.nCount = 0;
SetRect(&pData->rdh.rcBound, MAXLONG, MAXLONG, 0, 0);
}
}
}
/* Go to next row */
data += bytesPerLine;
}
/* Create or extend the region with the remaining rectangles*/
HRGN h = ExtCreateRegion(NULL, sizeof(RGNDATAHEADER)
+ (sizeof(RECT)*maxRects), pData);
if (hRgn) {
CombineRgn(hRgn, hRgn, h, RGN_OR);
DeleteObject(h);
} else hRgn = h;
free(pData); // I've created the region so I can free this now, right?
return hRgn;
}
void Fl_WinAPI_Window_Driver::draw() {
if (shape_data_) {
if ((shape_data_->lw_ != pWindow->w() || shape_data_->lh_ != pWindow->h()) && shape_data_->shape_) {
// size of window has changed since last time
shape_data_->lw_ = pWindow->w();
shape_data_->lh_ = pWindow->h();
Fl_Image* temp = shape_data_->shape_->copy(shape_data_->lw_, shape_data_->lh_);
HRGN region = bitmap2region(temp);
SetWindowRgn(fl_xid(pWindow), region, TRUE); // the system deletes the region when it's no longer needed
delete temp;
}
} Fl_Window_Driver::draw();
}
//
// End of "$Id$".

View File

@ -42,15 +42,28 @@
? where do we handle the interface between OpenGL/DirectX and Cocoa/WIN32/Glx?
*/
struct Fl_Window_Driver::shape_data_type {
int lw_; ///< width of shape image
int lh_; ///< height of shape image
Fl_Image* shape_; ///< shape image
Fl_Bitmap *todelete_; ///< auxiliary bitmap image
};
class FL_EXPORT Fl_X11_Window_Driver : public Fl_Window_Driver
{
friend class Fl_X;
private:
void combine_mask();
void shape_bitmap_(Fl_Image* b);
void shape_alpha_(Fl_Image* img, int offset);
public:
Fl_X11_Window_Driver(Fl_Window*);
virtual void take_focus();
virtual void shape(const Fl_Image* img);
virtual void draw();
};
#endif // FL_X11_WINDOW_DRIVER_H
//

View File

@ -20,8 +20,16 @@
#include "../../config_lib.h"
#include "Fl_X11_Window_Driver.H"
#include <FL/fl_draw.H>
#include <string.h>
#if HAVE_DLFCN_H
#include <dlfcn.h>
#endif
#define ShapeBounding 0
#define ShapeSet 0
#if USE_XDBE
#include <X11/extensions/Xdbe.h>
static int can_xdbe(); // forward
// class to be used only if Xdbe is used
@ -31,7 +39,7 @@ public:
virtual int double_flush(int eraseoverlay);
virtual void destroy_double_buffer();
};
#endif
#endif // USE_XDBE
Fl_Window_Driver *Fl_Window_Driver::newWindowDriver(Fl_Window *w)
@ -68,6 +76,7 @@ static int can_xdbe() { // whether the Xdbe extension is usable
if (!tried) {
tried = 1;
int event_base, error_base;
fl_open_display();
if (!XdbeQueryExtension(fl_display, &event_base, &error_base)) return 0;
Drawable root = RootWindow(fl_display,fl_screen);
int numscreens = 1;
@ -116,9 +125,108 @@ void Fl_X11_Dbe_Window_Driver::destroy_double_buffer() {
XdbeDeallocateBackBufferName(fl_display, i->other_xid);
i->other_xid = 0;
}
#endif // USE_XDBE
void Fl_X11_Window_Driver::shape_bitmap_(Fl_Image* b) {
shape_data_->shape_ = b;
}
void Fl_X11_Window_Driver::shape_alpha_(Fl_Image* img, int offset) {
int i, j, d = img->d(), w = img->w(), h = img->h(), bytesperrow = (w+7)/8;
unsigned u;
uchar byte, onebit;
// build an Fl_Bitmap covering the non-fully transparent/black part of the image
const uchar* bits = new uchar[h*bytesperrow]; // to store the bitmap
const uchar* alpha = (const uchar*)*img->data() + offset; // points to alpha value of rgba pixels
for (i = 0; i < h; i++) {
uchar *p = (uchar*)bits + i * bytesperrow;
byte = 0;
onebit = 1;
for (j = 0; j < w; j++) {
if (d == 3) {
u = *alpha;
u += *(alpha+1);
u += *(alpha+2);
}
else u = *alpha;
if (u > 0) { // if the pixel is not fully transparent/black
byte |= onebit; // turn on the corresponding bit of the bitmap
}
onebit = onebit << 1; // move the single set bit one position to the left
if (onebit == 0 || j == w-1) {
onebit = 1;
*p++ = byte; // store in bitmap one pack of bits
byte = 0;
}
alpha += d; // point to alpha value of next pixel
}
}
Fl_Bitmap* bitmap = new Fl_Bitmap(bits, w, h);
bitmap->alloc_array = 1;
shape_bitmap_(bitmap);
shape_data_->todelete_ = bitmap;
}
void Fl_X11_Window_Driver::shape(const Fl_Image* img) {
if (shape_data_) {
if (shape_data_->todelete_) { delete shape_data_->todelete_; }
}
else {
shape_data_ = new shape_data_type;
}
memset(shape_data_, 0, sizeof(shape_data_type));
pWindow->border(false);
int d = img->d();
if (d && img->count() >= 2) shape_pixmap_((Fl_Image*)img);
else if (d == 0) shape_bitmap_((Fl_Image*)img);
else if (d == 2 || d == 4) shape_alpha_((Fl_Image*)img, d - 1);
else if ((d == 1 || d == 3) && img->count() == 1) shape_alpha_((Fl_Image*)img, 0);
}
void Fl_X11_Window_Driver::combine_mask()
{
typedef void (*XShapeCombineMask_type)(Display*, int, int, int, int, Pixmap, int);
static XShapeCombineMask_type XShapeCombineMask_f = NULL;
static int beenhere = 0;
typedef Bool (*XShapeQueryExtension_type)(Display*, int*, int*);
if (!beenhere) {
beenhere = 1;
#if HAVE_DLSYM && HAVE_DLFCN_H
fl_open_display();
void *handle = dlopen(NULL, RTLD_LAZY); // search symbols in executable
XShapeQueryExtension_type XShapeQueryExtension_f = (XShapeQueryExtension_type)dlsym(handle, "XShapeQueryExtension");
XShapeCombineMask_f = (XShapeCombineMask_type)dlsym(handle, "XShapeCombineMask");
// make sure that the X server has the SHAPE extension
int error_base, shapeEventBase;
if ( !( XShapeQueryExtension_f && XShapeCombineMask_f &&
XShapeQueryExtension_f(fl_display, &shapeEventBase, &error_base) ) ) XShapeCombineMask_f = NULL;
#endif
}
if (!XShapeCombineMask_f) return;
shape_data_->lw_ = pWindow->w();
shape_data_->lh_ = pWindow->h();
Fl_Image* temp = shape_data_->shape_->copy(shape_data_->lw_, shape_data_->lh_);
Pixmap pbitmap = XCreateBitmapFromData(fl_display, fl_xid(pWindow),
(const char*)*temp->data(),
temp->w(), temp->h());
XShapeCombineMask_f(fl_display, fl_xid(pWindow), ShapeBounding, 0, 0, pbitmap, ShapeSet);
if (pbitmap != None) XFreePixmap(fl_display, pbitmap);
delete temp;
}
void Fl_X11_Window_Driver::draw() {
if (shape_data_) {
if (( shape_data_->lw_ != pWindow->w() || shape_data_->lh_ != pWindow->h() ) && shape_data_->shape_) {
// size of window has changed since last time
combine_mask();
}
}
Fl_Window_Driver::draw();
}
//
// End of "$Id$".
//