New member function Fl_Image_Surface::mask(Fl_RGB_Image*)

This commit is contained in:
ManoloFLTK 2023-11-20 17:13:09 +01:00
parent b9ac6bd728
commit 3e61ec7044
15 changed files with 564 additions and 14 deletions

View File

@ -82,6 +82,7 @@ public:
int printable_rect(int *w, int *h) FL_OVERRIDE; int printable_rect(int *w, int *h) FL_OVERRIDE;
Fl_Offscreen offscreen(); Fl_Offscreen offscreen();
void rescale(); void rescale();
void mask(const Fl_RGB_Image *);
}; };
@ -107,11 +108,15 @@ protected:
int external_offscreen; int external_offscreen;
Fl_Image_Surface_Driver(int w, int h, int /*high_res*/, Fl_Offscreen off) : Fl_Widget_Surface(NULL), width(w), height(h), offscreen(off) {external_offscreen = (off != 0);} Fl_Image_Surface_Driver(int w, int h, int /*high_res*/, Fl_Offscreen off) : Fl_Widget_Surface(NULL), width(w), height(h), offscreen(off) {external_offscreen = (off != 0);}
virtual ~Fl_Image_Surface_Driver() {} virtual ~Fl_Image_Surface_Driver() {}
static void copy_with_mask(Fl_RGB_Image* mask, uchar *dib_dst, uchar *dib_src,
int line_size, bool bottom_to_top);
static Fl_RGB_Image *RGB3_to_RGB1(const Fl_RGB_Image *rgb3, int W, int H);
void set_current() FL_OVERRIDE = 0; void set_current() FL_OVERRIDE = 0;
void translate(int x, int y) FL_OVERRIDE = 0; void translate(int x, int y) FL_OVERRIDE = 0;
void untranslate() FL_OVERRIDE = 0; void untranslate() FL_OVERRIDE = 0;
int printable_rect(int *w, int *h) FL_OVERRIDE; int printable_rect(int *w, int *h) FL_OVERRIDE;
virtual Fl_RGB_Image *image() = 0; virtual Fl_RGB_Image *image() = 0;
virtual void mask(const Fl_RGB_Image *) {}
/** Each platform implements this function its own way. /** Each platform implements this function its own way.
It returns an object implementing all virtual functions It returns an object implementing all virtual functions
of class Fl_Image_Surface_Driver for the plaform. of class Fl_Image_Surface_Driver for the plaform.

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB

View File

@ -90,15 +90,63 @@ int Fl_Image_Surface_Driver::printable_rect(int *w, int *h) {
return 0; return 0;
} }
// used by the Windows and X11(no Cairo) platforms
void Fl_Image_Surface_Driver::copy_with_mask(Fl_RGB_Image* mask, uchar *dib_dst,
uchar *dib_src, int line_size,
bool bottom_to_top) {
int w = mask->data_w(), h = mask->data_h();
for (int i = 0; i < h; i++) {
const uchar* alpha = (const uchar*)mask->array +
(bottom_to_top ? (h-i-1) : i) * w;
uchar *src = dib_src + i * line_size;
uchar *dst = dib_dst + i * line_size;
for (int j = 0; j < w; j++) {
// mix src and dst into dst weighted by mask pixel's value
uchar u = *alpha++, v = 255 - u;
*dst = ((*dst) * v + (*src) * u)/255;
dst++; src++;
*dst = ((*dst) * v + (*src) * u)/255;
dst++; src++;
*dst = ((*dst) * v + (*src) * u)/255;
dst++; src++;
}
}
}
Fl_RGB_Image *Fl_Image_Surface_Driver::RGB3_to_RGB1(const Fl_RGB_Image *rgb3, int W, int H) {
bool need_copy = false;
if (W != rgb3->data_w() || H != rgb3->data_h()) {
rgb3 = (Fl_RGB_Image*)rgb3->copy(W, H);
need_copy = true;
}
uchar *data = new uchar[W * H];
int i, j, ld = rgb3->ld();
if (!ld) ld = 3 * W;
uchar *p = data;
for (i = 0; i < H; i++) {
const uchar* alpha = rgb3->array + i * ld;
for (j = 0; j < W; j++) {
*p++ = (*alpha + *(alpha+1) + *(alpha+2)) / 3;
alpha += 3;
}
}
Fl_RGB_Image *rgb1 = new Fl_RGB_Image(data, W, H, 1);
rgb1->alloc_array = 1;
if (need_copy) delete rgb3;
return rgb1;
}
/** /**
\} \}
\endcond \endcond
*/ */
/** Returns a depth 3 image made of all drawings sent to the Fl_Image_Surface object. /** Returns a depth-3 image made of all drawings sent to the Fl_Image_Surface object.
The returned object contains its own copy of the RGB data;
The returned object contains its own copy of the RGB data. the caller is responsible for deleting it.
The caller is responsible for deleting the image.
\see Fl_Image_Surface::mask(Fl_RGB_Image*)
*/ */
Fl_RGB_Image *Fl_Image_Surface::image() { Fl_RGB_Image *Fl_Image_Surface::image() {
bool need_push = (Fl_Surface_Device::surface() != platform_surface); bool need_push = (Fl_Surface_Device::surface() != platform_surface);
@ -152,6 +200,44 @@ void Fl_Image_Surface::rescale() {
} }
/** Defines a mask applied to drawings made after use of this function.
The mask is an Fl_RGB_Image made of a white scene drawn on a solid black
background; the drawable part of the image surface is reduced to the white areas of the mask
after this member function gets called. If necessary, the \p mask image is internally
replaced by a copy resized to the surface's pixel size.
Overall, the image returned by Fl_Image_Surface::image() contains all drawings made
until the mask() method assigned a mask, at which point subsequent drawing operations
to the image surface were passed through the white areas of the mask.
On some platforms, shades of gray in the mask image control the blending of
foreground and background pixels; mask pixels closer in color to white produce image pixels
closer to the image surface pixel, those closer to black produce image pixels closer to what the
image surface pixel was before the call to mask().
The mask is easily constructed using an Fl_Image_Surface object,
drawing white areas on a black background there, and calling Fl_Image_Surface::image().
\param mask A depth-3 image determining the drawable areas of the image surface.
The \p mask object is not used after return from this member function.
\note The image surface must not be the current drawing surface when this function
gets called. The mask can have any size but is best when it has the size of the image surface.
A typical procedure is to use the image surface to draw first the mask (using white over black),
call Fl_Image_Surface::image() to obtain the mask, then draw the background, call
Fl_Image_Surface::mask(mask), draw the foreground, and finally get the resulting
image from Fl_Image_Surface::image().
It's possible to use several masks in succession on the same image surface provided
member function Fl_Image_Surface::image() is called between successive calls to
Fl_Image_Surface::mask(Fl_RGB_Image*).
This diagram depicts operations involved in the construction of a masked image:
\image html masked_image.png "Construction of a masked image"
\image latex masked_image.png "Construction of a masked image" width=8cm
\since 1.4.0
*/
void Fl_Image_Surface::mask(const Fl_RGB_Image *mask) {
platform_surface->mask(mask);
}
// implementation of the fl_XXX_offscreen() functions // implementation of the fl_XXX_offscreen() functions
static Fl_Image_Surface **offscreen_api_surface = NULL; static Fl_Image_Surface **offscreen_api_surface = NULL;

View File

@ -68,6 +68,7 @@ public:
cairo_t *cr() { return cairo_; } cairo_t *cr() { return cairo_; }
PangoLayout *pango_layout() {return pango_layout_;} PangoLayout *pango_layout() {return pango_layout_;}
void set_cairo(cairo_t *c, float f = 0); void set_cairo(cairo_t *c, float f = 0);
static cairo_pattern_t *calc_cairo_mask(const Fl_RGB_Image *rgb);
void check_status(void); void check_status(void);

View File

@ -1517,4 +1517,41 @@ void Fl_Cairo_Graphics_Driver::focus_rect(int x, int y, int w, int h)
surface_needs_commit(); surface_needs_commit();
} }
cairo_pattern_t *Fl_Cairo_Graphics_Driver::calc_cairo_mask(const Fl_RGB_Image *rgb) {
int i, j, d = rgb->d(), w = rgb->data_w(), h = rgb->data_h(), ld = rgb->ld();
int bytesperrow = cairo_format_stride_for_width(CAIRO_FORMAT_A1, w);
if (!ld) ld = d * w;
unsigned u;
uchar byte, onebit;
// build a CAIRO_FORMAT_A1 surface covering the non-black part of the image
uchar* bits = new uchar[h*bytesperrow]; // to store the surface data
for (i = 0; i < h; i++) {
const uchar* alpha = (const uchar*)*rgb->data() + i * ld;
uchar *p = (uchar*)bits + i * bytesperrow;
byte = 0;
onebit = 1;
for (j = 0; j < w; j++) {
u = *alpha;
u += *(alpha+1);
u += *(alpha+2);
if (u > 0) { // if the pixel is not 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 next rgb pixel
}
}
cairo_surface_t *mask_surf = cairo_image_surface_create_for_data(bits,
CAIRO_FORMAT_A1, w, h, bytesperrow);
cairo_pattern_t *mask_pattern = cairo_pattern_create_for_surface(mask_surf);
cairo_surface_destroy(mask_surf);
return mask_pattern;
}
#endif // USE_PANGO #endif // USE_PANGO

View File

@ -70,6 +70,7 @@ public:
// --- bitmap stuff // --- bitmap stuff
static HBITMAP create_bitmask(int w, int h, const uchar *array); // NOT virtual static HBITMAP create_bitmask(int w, int h, const uchar *array); // NOT virtual
static HBITMAP calc_HBITMAP_mask(Fl_RGB_Image *mask);
void delete_bitmask(fl_uintptr_t bm) FL_OVERRIDE; void delete_bitmask(fl_uintptr_t bm) FL_OVERRIDE;
HBITMAP create_alphamask(int w, int h, int d, int ld, const uchar *array); HBITMAP create_alphamask(int w, int h, int d, int ld, const uchar *array);
void draw_unscaled(const char* str, int n, int x, int y) FL_OVERRIDE; void draw_unscaled(const char* str, int n, int x, int y) FL_OVERRIDE;

View File

@ -26,6 +26,12 @@ class Fl_GDI_Image_Surface_Driver : public Fl_Image_Surface_Driver {
public: public:
HWND pre_window; HWND pre_window;
int _savedc; int _savedc;
void mask(const Fl_RGB_Image *) FL_OVERRIDE;
struct shape_data_type {
HBITMAP background;
uchar *vBits;
Fl_RGB_Image* mask;
} *shape_data_;
Fl_GDI_Image_Surface_Driver(int w, int h, int high_res, Fl_Offscreen off); Fl_GDI_Image_Surface_Driver(int w, int h, int high_res, Fl_Offscreen off);
~Fl_GDI_Image_Surface_Driver(); ~Fl_GDI_Image_Surface_Driver();
void set_current() FL_OVERRIDE; void set_current() FL_OVERRIDE;

View File

@ -19,6 +19,7 @@
#include "../WinAPI/Fl_WinAPI_Screen_Driver.H" #include "../WinAPI/Fl_WinAPI_Screen_Driver.H"
#include "Fl_GDI_Image_Surface_Driver.H" #include "Fl_GDI_Image_Surface_Driver.H"
#include <FL/platform.H> #include <FL/platform.H>
#include <FL/Fl_Bitmap.H>
#include <windows.h> #include <windows.h>
@ -35,10 +36,16 @@ Fl_GDI_Image_Surface_Driver::Fl_GDI_Image_Surface_Driver(int w, int h, int high_
driver(Fl_Graphics_Driver::newMainGraphicsDriver()); driver(Fl_Graphics_Driver::newMainGraphicsDriver());
if (d != 1 && high_res) ((Fl_GDI_Graphics_Driver*)driver())->scale(d); if (d != 1 && high_res) ((Fl_GDI_Graphics_Driver*)driver())->scale(d);
origin.x = origin.y = 0; origin.x = origin.y = 0;
shape_data_ = NULL;
} }
Fl_GDI_Image_Surface_Driver::~Fl_GDI_Image_Surface_Driver() { Fl_GDI_Image_Surface_Driver::~Fl_GDI_Image_Surface_Driver() {
if (shape_data_ && shape_data_->background) {
DeleteObject(shape_data_->background);
delete shape_data_->mask;
free(shape_data_);
}
if (offscreen && !external_offscreen) DeleteObject((HBITMAP)offscreen); if (offscreen && !external_offscreen) DeleteObject((HBITMAP)offscreen);
delete driver(); delete driver();
} }
@ -67,6 +74,43 @@ void Fl_GDI_Image_Surface_Driver::untranslate() {
Fl_RGB_Image* Fl_GDI_Image_Surface_Driver::image() Fl_RGB_Image* Fl_GDI_Image_Surface_Driver::image()
{ {
if (shape_data_ && shape_data_->background) {
// get the offscreen size in pixels
HDC gc = fl_makeDC((HBITMAP)offscreen);
BITMAPINFO bmi;
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 0;
bmi.bmiHeader.biSizeImage = 0;
GetDIBits(gc, (HBITMAP)offscreen, 0, 0, NULL, &bmi, DIB_RGB_COLORS);
int W = bmi.bmiHeader.biWidth;
int H = bmi.bmiHeader.biHeight;
int line_size = ((3*W+3)/4) * 4;
// read bits of main offscreen
uchar *dib_src = new uchar[line_size * H];
bmi.bmiHeader.biWidth = W;
bmi.bmiHeader.biHeight = H;
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biBitCount = 24;
GetDIBits(gc, (HBITMAP)offscreen, 0, H,
dib_src, &bmi, DIB_RGB_COLORS);
// draw above the secondary offscreen the main offscreen masked by shape_data_->mask
GdiFlush();
Fl_Image_Surface_Driver::copy_with_mask(shape_data_->mask, shape_data_->vBits, dib_src, ((3*W+3)/4) * 4, true);
delete shape_data_->mask;
delete[] dib_src;
// write bits of main offscreen
SetDIBits(gc, (HBITMAP)offscreen, 0, H, shape_data_->vBits, &bmi, DIB_RGB_COLORS);
DeleteDC(gc);
DeleteObject(shape_data_->background);
shape_data_->background = NULL;
free(shape_data_);
shape_data_ = NULL;
}
Fl_RGB_Image *image = Fl::screen_driver()->read_win_rectangle( 0, 0, width, height, 0); Fl_RGB_Image *image = Fl::screen_driver()->read_win_rectangle( 0, 0, width, height, 0);
return image; return image;
} }
@ -81,3 +125,45 @@ void Fl_GDI_Image_Surface_Driver::end_current()
fl_window = pre_window; fl_window = pre_window;
Fl_Surface_Device::end_current(); Fl_Surface_Device::end_current();
} }
void Fl_GDI_Image_Surface_Driver::mask(const Fl_RGB_Image *mask) {
shape_data_ = (struct shape_data_type*)calloc(1, sizeof(struct shape_data_type));
// get the offscreen size in pixels
HDC gc = fl_makeDC((HBITMAP)offscreen);
BITMAPINFO bmi;
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 0;
bmi.bmiHeader.biSizeImage = 0;
GetDIBits(gc, (HBITMAP)offscreen, 0, 0, NULL, &bmi, DIB_RGB_COLORS);
int W = bmi.bmiHeader.biWidth;
int H = bmi.bmiHeader.biHeight;
shape_data_->mask = Fl_Image_Surface_Driver::RGB3_to_RGB1(mask, W, H);
// duplicate current offscreen content to new offscreen
int line_size = ((3*W+3)/4) * 4;
uchar *dib = new uchar[line_size * H]; // create temporary buffer to read DIB
bmi.bmiHeader.biWidth = W;
bmi.bmiHeader.biHeight = H;
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biBitCount = 24;
GetDIBits(gc, (HBITMAP)offscreen, 0, H, dib, &bmi, DIB_RGB_COLORS);
HDC background_gc = CreateCompatibleDC(gc);
shape_data_->background =
CreateDIBSection(background_gc, &bmi, DIB_RGB_COLORS,
(void**)&shape_data_->vBits, NULL, 0);
if (!shape_data_->background) {
Fl::error("CreateDIBSection error=%lu", GetLastError());
}
memcpy(shape_data_->vBits, dib, H * line_size);
delete[] dib;
DeleteDC(background_gc);
DeleteDC(gc);
}

View File

@ -21,6 +21,11 @@
#include <FL/platform.H> #include <FL/platform.H>
class Fl_Quartz_Image_Surface_Driver : public Fl_Image_Surface_Driver { class Fl_Quartz_Image_Surface_Driver : public Fl_Image_Surface_Driver {
private:
# if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
CGImageRef mask_;
void mask(const Fl_RGB_Image *) FL_OVERRIDE;
#endif
void end_current() FL_OVERRIDE; void end_current() FL_OVERRIDE;
public: public:
FLWindow *pre_window; FLWindow *pre_window;

View File

@ -1,7 +1,7 @@
// //
// Draw-to-image code for the Fast Light Tool Kit (FLTK). // Draw-to-image code for the Fast Light Tool Kit (FLTK).
// //
// Copyright 1998-2018 by Bill Spitzak and others. // Copyright 1998-2023 by Bill Spitzak and others.
// //
// This library is free software. Distribution and use rights are outlined in // This library is free software. Distribution and use rights are outlined in
// the file "COPYING" which should have been included with this file. If this // the file "COPYING" which should have been included with this file. If this
@ -23,6 +23,9 @@
Fl_Quartz_Image_Surface_Driver::Fl_Quartz_Image_Surface_Driver(int w, int h, int high_res, Fl_Offscreen off) : Fl_Image_Surface_Driver(w, h, high_res, off) { Fl_Quartz_Image_Surface_Driver::Fl_Quartz_Image_Surface_Driver(int w, int h, int high_res, Fl_Offscreen off) : Fl_Image_Surface_Driver(w, h, high_res, off) {
# if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
mask_ = NULL;
#endif
int W = w, H = h; int W = w, H = h;
float s = 1; float s = 1;
if (high_res) { if (high_res) {
@ -49,6 +52,12 @@ Fl_Quartz_Image_Surface_Driver::Fl_Quartz_Image_Surface_Driver(int w, int h, int
} }
Fl_Quartz_Image_Surface_Driver::~Fl_Quartz_Image_Surface_Driver() { Fl_Quartz_Image_Surface_Driver::~Fl_Quartz_Image_Surface_Driver() {
# if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
if (mask_) {
CGImageRelease(mask_);
}
#endif
if (offscreen) CGContextRestoreGState((CGContextRef)offscreen);
if (offscreen && !external_offscreen) { if (offscreen && !external_offscreen) {
void *data = CGBitmapContextGetData((CGContextRef)offscreen); void *data = CGBitmapContextGetData((CGContextRef)offscreen);
free(data); free(data);
@ -56,6 +65,7 @@ Fl_Quartz_Image_Surface_Driver::~Fl_Quartz_Image_Surface_Driver() {
} }
delete driver(); delete driver();
} }
void Fl_Quartz_Image_Surface_Driver::set_current() { void Fl_Quartz_Image_Surface_Driver::set_current() {
Fl_Surface_Device::set_current(); Fl_Surface_Device::set_current();
@ -63,6 +73,15 @@ void Fl_Quartz_Image_Surface_Driver::set_current() {
driver()->gc((CGContextRef)offscreen); driver()->gc((CGContextRef)offscreen);
fl_window = 0; fl_window = 0;
((Fl_Quartz_Graphics_Driver*)driver())->high_resolution( CGBitmapContextGetWidth((CGContextRef)offscreen) > (size_t)width ); ((Fl_Quartz_Graphics_Driver*)driver())->high_resolution( CGBitmapContextGetWidth((CGContextRef)offscreen) > (size_t)width );
# if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
if (mask_) {
int W, H;
printable_rect(&W, &H);
CGContextSaveGState((CGContextRef)offscreen);
CGContextClipToMask((CGContextRef)offscreen, CGRectMake(0,0,W,H), mask_); // 10.4
CGContextSaveGState((CGContextRef)offscreen);
}
# endif
} }
void Fl_Quartz_Image_Surface_Driver::translate(int x, int y) { void Fl_Quartz_Image_Surface_Driver::translate(int x, int y) {
@ -79,6 +98,13 @@ void Fl_Quartz_Image_Surface_Driver::untranslate() {
Fl_RGB_Image* Fl_Quartz_Image_Surface_Driver::image() Fl_RGB_Image* Fl_Quartz_Image_Surface_Driver::image()
{ {
CGContextFlush((CGContextRef)offscreen); CGContextFlush((CGContextRef)offscreen);
# if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
if (mask_) {
CGContextRestoreGState((CGContextRef)offscreen);
CGImageRelease(mask_);
mask_ = NULL;
}
#endif
int W = (int)CGBitmapContextGetWidth((CGContextRef)offscreen); int W = (int)CGBitmapContextGetWidth((CGContextRef)offscreen);
int H = (int)CGBitmapContextGetHeight((CGContextRef)offscreen); int H = (int)CGBitmapContextGetHeight((CGContextRef)offscreen);
int bpr = (int)CGBitmapContextGetBytesPerRow((CGContextRef)offscreen); int bpr = (int)CGBitmapContextGetBytesPerRow((CGContextRef)offscreen);
@ -101,6 +127,54 @@ Fl_RGB_Image* Fl_Quartz_Image_Surface_Driver::image()
void Fl_Quartz_Image_Surface_Driver::end_current() void Fl_Quartz_Image_Surface_Driver::end_current()
{ {
# if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
if (mask_) {
CGContextRestoreGState((CGContextRef)offscreen);
CGContextRestoreGState((CGContextRef)offscreen);
}
# endif
fl_window = pre_window; fl_window = pre_window;
Fl_Surface_Device::end_current(); Fl_Surface_Device::end_current();
} }
# if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
static void MyProviderReleaseData (void *info, const void *data, size_t size) {
delete[] (uchar*)data;
}
void Fl_Quartz_Image_Surface_Driver::mask(const Fl_RGB_Image *img) {
if (!&CGContextClipToMask) return;
int W = (int)CGBitmapContextGetWidth((CGContextRef)offscreen);
int H = (int)CGBitmapContextGetHeight((CGContextRef)offscreen);
bool using_copy = false;
if (W != img->data_w() || H != img->data_h()) {
Fl_RGB_Image *copy = (Fl_RGB_Image*)img->copy(W, H);
img = copy;
using_copy = true;
}
int i, d = img->d(), w = img->data_w(), h = img->data_h();
// reverse top and bottom and convert to gray scale if img->d() == 3 and complement bits
int bytes_per_row = (img->ld() ? img->ld() : w * d);
uchar *from = new uchar[w * h];
for ( i = 0; i < h; i++) {
const uchar *p = img->array + bytes_per_row * i;
const uchar *last = p + bytes_per_row;
uchar *q = from + (h - 1 - i) * w;
while (p < last) {
unsigned u = *p++;
u += *p++;
u += *p++;
*q++ = ~(u/3);
}
}
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, from, w * h, MyProviderReleaseData);
mask_ = CGImageMaskCreate(w, h, 8, 8, w, provider, NULL, false);
CFRelease(provider);
if (using_copy) delete img;
}
#endif

View File

@ -25,6 +25,12 @@ class Fl_Wayland_Image_Surface_Driver : public Fl_Image_Surface_Driver {
public: public:
Fl_Wayland_Image_Surface_Driver(int w, int h, int high_res, Fl_Offscreen off); Fl_Wayland_Image_Surface_Driver(int w, int h, int high_res, Fl_Offscreen off);
~Fl_Wayland_Image_Surface_Driver(); ~Fl_Wayland_Image_Surface_Driver();
void mask(const Fl_RGB_Image *) FL_OVERRIDE;
struct shape_data_type {
double scale;
cairo_pattern_t *mask_pattern_;
cairo_t *bg_cr;
} *shape_data_;
void set_current() FL_OVERRIDE; void set_current() FL_OVERRIDE;
void translate(int x, int y) FL_OVERRIDE; void translate(int x, int y) FL_OVERRIDE;
void untranslate() FL_OVERRIDE; void untranslate() FL_OVERRIDE;

View File

@ -22,6 +22,7 @@
Fl_Wayland_Image_Surface_Driver::Fl_Wayland_Image_Surface_Driver(int w, int h, Fl_Wayland_Image_Surface_Driver::Fl_Wayland_Image_Surface_Driver(int w, int h,
int high_res, Fl_Offscreen off) : Fl_Image_Surface_Driver(w, h, high_res, off) { int high_res, Fl_Offscreen off) : Fl_Image_Surface_Driver(w, h, high_res, off) {
shape_data_ = NULL;
float s = 1; float s = 1;
int d = 1; int d = 1;
if (!off) { if (!off) {
@ -51,6 +52,19 @@ Fl_Wayland_Image_Surface_Driver::Fl_Wayland_Image_Surface_Driver(int w, int h,
Fl_Wayland_Image_Surface_Driver::~Fl_Wayland_Image_Surface_Driver() { Fl_Wayland_Image_Surface_Driver::~Fl_Wayland_Image_Surface_Driver() {
if (shape_data_) {
cairo_surface_t *surf;
cairo_pattern_get_surface(shape_data_->mask_pattern_, &surf);
unsigned char *bits = cairo_image_surface_get_data(surf);
cairo_pattern_destroy(shape_data_->mask_pattern_);
delete[] bits;
struct Fl_Wayland_Graphics_Driver::draw_buffer *off_ =
Fl_Wayland_Graphics_Driver::offscreen_buffer((Fl_Offscreen)shape_data_->bg_cr);
delete[] off_->buffer;
free(off_);
cairo_destroy(shape_data_->bg_cr);
free(shape_data_);
}
if (offscreen && !external_offscreen) { if (offscreen && !external_offscreen) {
struct Fl_Wayland_Graphics_Driver::draw_buffer *buffer = struct Fl_Wayland_Graphics_Driver::draw_buffer *buffer =
Fl_Wayland_Graphics_Driver::offscreen_buffer(offscreen); Fl_Wayland_Graphics_Driver::offscreen_buffer(offscreen);
@ -91,10 +105,38 @@ void Fl_Wayland_Image_Surface_Driver::untranslate() {
Fl_RGB_Image* Fl_Wayland_Image_Surface_Driver::image() { Fl_RGB_Image* Fl_Wayland_Image_Surface_Driver::image() {
if (shape_data_ && shape_data_->mask_pattern_) {
// draw above the secondary offscreen the main offscreen masked by mask_pattern_
cairo_t *c = ((Fl_Cairo_Graphics_Driver*)driver())->cr();
cairo_pattern_t *paint_pattern = cairo_pattern_create_for_surface(cairo_get_target(c));
cairo_set_source(shape_data_->bg_cr, paint_pattern);
cairo_mask(shape_data_->bg_cr, shape_data_->mask_pattern_);
cairo_pattern_destroy(paint_pattern);
// copy secondary offscreen to the main offscreen
cairo_pattern_t *pat = cairo_pattern_create_for_surface(cairo_get_target(shape_data_->bg_cr));
cairo_scale(c, shape_data_->scale, shape_data_->scale);
cairo_set_source(c, pat),
cairo_paint(c);
cairo_pattern_destroy(pat);
// delete secondary offscreen
cairo_surface_t *surf;
cairo_pattern_get_surface(shape_data_->mask_pattern_, &surf);
unsigned char *bits = cairo_image_surface_get_data(surf);
cairo_pattern_destroy(shape_data_->mask_pattern_);
delete[] bits;
struct Fl_Wayland_Graphics_Driver::draw_buffer *off_ =
Fl_Wayland_Graphics_Driver::offscreen_buffer((Fl_Offscreen)shape_data_->bg_cr);
delete[] off_->buffer;
free(off_);
cairo_destroy(shape_data_->bg_cr);
free(shape_data_);
shape_data_ = NULL;
}
// Convert depth-4 image in draw_buffer to a depth-3 image while exchanging R and B colors // Convert depth-4 image in draw_buffer to a depth-3 image while exchanging R and B colors
struct Fl_Wayland_Graphics_Driver::draw_buffer *off_buf = struct Fl_Wayland_Graphics_Driver::draw_buffer *off_buf =
Fl_Wayland_Graphics_Driver::offscreen_buffer(offscreen); Fl_Wayland_Graphics_Driver::offscreen_buffer(offscreen);
int height = off_buf->data_size / off_buf->stride; int height = int(off_buf->data_size / off_buf->stride);
uchar *rgb = new uchar[off_buf->width * height * 3]; uchar *rgb = new uchar[off_buf->width * height * 3];
uchar *p = rgb; uchar *p = rgb;
uchar *q; uchar *q;
@ -111,3 +153,34 @@ Fl_RGB_Image* Fl_Wayland_Image_Surface_Driver::image() {
image->alloc_array = 1; image->alloc_array = 1;
return image; return image;
} }
void Fl_Wayland_Image_Surface_Driver::mask(const Fl_RGB_Image *mask) {
bool using_copy = false;
shape_data_ = (struct shape_data_type*)calloc(1, sizeof(struct shape_data_type));
int W, H;
struct Fl_Wayland_Graphics_Driver::draw_buffer *off_buf =
Fl_Wayland_Graphics_Driver::offscreen_buffer(offscreen);
W = off_buf->width;
H = (int)(off_buf->data_size / off_buf->stride);
if (W != mask->data_w() || H != mask->data_h()) {
Fl_RGB_Image *copy = (Fl_RGB_Image*)mask->copy(W, H);
mask = copy;
using_copy = true;
}
shape_data_->mask_pattern_ = Fl_Cairo_Graphics_Driver::calc_cairo_mask(mask);
//duplicate current offscreen content to new cairo_t* shape_data_->bg_cr
int width, height;
printable_rect(&width, &height);
struct Fl_Wayland_Graphics_Driver::draw_buffer *off_ =
(struct Fl_Wayland_Graphics_Driver::draw_buffer*)calloc(1,
sizeof(struct Fl_Wayland_Graphics_Driver::draw_buffer));
Fl_Wayland_Graphics_Driver::cairo_init(off_, W, H,
cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, W),
CAIRO_FORMAT_RGB24);
cairo_set_user_data(off_->cairo_, &Fl_Wayland_Graphics_Driver::key, off_, NULL);
shape_data_->bg_cr = off_->cairo_;
memcpy(off_->buffer, off_buf->buffer, off_buf->data_size);
shape_data_->scale = double(width) / W;
if (using_copy) delete mask;
}

View File

@ -1,7 +1,7 @@
// //
// Draw-to-image code for the Fast Light Tool Kit (FLTK). // Draw-to-image code for the Fast Light Tool Kit (FLTK).
// //
// Copyright 2022 by Bill Spitzak and others. // Copyright 2022-2023 by Bill Spitzak and others.
// //
// This library is free software. Distribution and use rights are outlined in // This library is free software. Distribution and use rights are outlined in
// the file "COPYING" which should have been included with this file. If this // the file "COPYING" which should have been included with this file. If this
@ -32,8 +32,19 @@ public:
void translate(int x, int y) FL_OVERRIDE; void translate(int x, int y) FL_OVERRIDE;
void untranslate() FL_OVERRIDE; void untranslate() FL_OVERRIDE;
Fl_RGB_Image *image() FL_OVERRIDE; Fl_RGB_Image *image() FL_OVERRIDE;
void mask(const Fl_RGB_Image *) FL_OVERRIDE;
#if FLTK_USE_CAIRO #if FLTK_USE_CAIRO
cairo_t *cairo_; cairo_t *cairo_;
struct shape_data_type {
double scale;
cairo_pattern_t *mask_pattern_;
cairo_t *bg_cr;
} *shape_data_;
#else
struct shape_data_type {
Pixmap background;
Fl_RGB_Image* mask;
} *shape_data_;
#endif #endif
}; };

View File

@ -17,6 +17,7 @@
#include <FL/platform.H> #include <FL/platform.H>
#include "Fl_Xlib_Image_Surface_Driver.H" #include "Fl_Xlib_Image_Surface_Driver.H"
#include "../../Fl_Screen_Driver.H" #include "../../Fl_Screen_Driver.H"
#include <stdlib.h>
#if FLTK_USE_CAIRO #if FLTK_USE_CAIRO
# include <cairo-xlib.h> # include <cairo-xlib.h>
# include "../Cairo/Fl_X11_Cairo_Graphics_Driver.H" # include "../Cairo/Fl_X11_Cairo_Graphics_Driver.H"
@ -37,6 +38,7 @@ Fl_Xlib_Image_Surface_Driver::Fl_Xlib_Image_Surface_Driver(int w, int h, int hig
} }
offscreen = (Fl_Offscreen)XCreatePixmap(fl_display, RootWindow(fl_display, fl_screen), w, h, fl_visual->depth); offscreen = (Fl_Offscreen)XCreatePixmap(fl_display, RootWindow(fl_display, fl_screen), w, h, fl_visual->depth);
} }
shape_data_ = NULL;
#if FLTK_USE_CAIRO #if FLTK_USE_CAIRO
driver(new Fl_X11_Cairo_Graphics_Driver()); driver(new Fl_X11_Cairo_Graphics_Driver());
cairo_surface_t *s = cairo_xlib_surface_create(fl_display, offscreen, fl_visual->visual, w, h); cairo_surface_t *s = cairo_xlib_surface_create(fl_display, offscreen, fl_visual->visual, w, h);
@ -52,7 +54,24 @@ Fl_Xlib_Image_Surface_Driver::Fl_Xlib_Image_Surface_Driver(int w, int h, int hig
Fl_Xlib_Image_Surface_Driver::~Fl_Xlib_Image_Surface_Driver() { Fl_Xlib_Image_Surface_Driver::~Fl_Xlib_Image_Surface_Driver() {
#if FLTK_USE_CAIRO #if FLTK_USE_CAIRO
if (shape_data_) {
cairo_surface_t *surf;
cairo_pattern_get_surface(shape_data_->mask_pattern_, &surf);
unsigned char *bits = cairo_image_surface_get_data(surf);
cairo_pattern_destroy(shape_data_->mask_pattern_);
delete[] bits;
Pixmap p = cairo_xlib_surface_get_drawable(cairo_get_target(shape_data_->bg_cr));
XFreePixmap(fl_display, p);
cairo_destroy(shape_data_->bg_cr);
free(shape_data_);
}
cairo_destroy(cairo_); cairo_destroy(cairo_);
#else
if (shape_data_) {
XFreePixmap(fl_display, shape_data_->background);
delete shape_data_->mask;
free(shape_data_);
}
#endif #endif
if (offscreen && !external_offscreen) XFreePixmap(fl_display, (Pixmap)offscreen); if (offscreen && !external_offscreen) XFreePixmap(fl_display, (Pixmap)offscreen);
delete driver(); delete driver();
@ -84,14 +103,117 @@ void Fl_Xlib_Image_Surface_Driver::untranslate() {
#endif #endif
} }
Fl_RGB_Image* Fl_Xlib_Image_Surface_Driver::image() Fl_RGB_Image* Fl_Xlib_Image_Surface_Driver::image()
{ {
if (shape_data_) {
#if FLTK_USE_CAIRO
// draw above the secondary offscreen the main offscreen masked by mask_pattern_
cairo_t *c = ((Fl_Cairo_Graphics_Driver*)driver())->cr();
cairo_pattern_t *paint_pattern = cairo_pattern_create_for_surface(cairo_get_target(c));
cairo_set_source(shape_data_->bg_cr, paint_pattern);
cairo_mask(shape_data_->bg_cr, shape_data_->mask_pattern_);
cairo_pattern_destroy(paint_pattern);
// copy secondary offscreen to the main offscreen
cairo_pattern_t *pat = cairo_pattern_create_for_surface(cairo_get_target(shape_data_->bg_cr));
cairo_scale(c, shape_data_->scale, shape_data_->scale);
cairo_set_source(c, pat),
cairo_paint(c);
cairo_pattern_destroy(pat);
// delete secondary offscreen
cairo_surface_t *surf;
cairo_pattern_get_surface(shape_data_->mask_pattern_, &surf);
unsigned char *bits = cairo_image_surface_get_data(surf);
cairo_pattern_destroy(shape_data_->mask_pattern_);
delete[] bits;
Pixmap p = cairo_xlib_surface_get_drawable(cairo_get_target(shape_data_->bg_cr));
XFreePixmap(fl_display, p);
cairo_destroy(shape_data_->bg_cr);
#else // !FLTK_USE_CAIRO
// draw the main offscreen masked by shape_data_->mask above the background offscreen
int w, h;
printable_rect(&w, &h);
Fl_RGB_Image *img_main = Fl::screen_driver()->read_win_rectangle(0, 0, w, h, 0);
fl_window = shape_data_->background; // temporary change
Fl_RGB_Image *img_background = Fl::screen_driver()->read_win_rectangle(0, 0, w, h, 0);
fl_window = offscreen;
Fl_Image_Surface_Driver::copy_with_mask(shape_data_->mask,
(uchar*)img_background->array,
(uchar*)img_main->array,
3 * shape_data_->mask->w(), false);
delete img_main;
// copy background offscreen to main offscreen
float s = driver()->scale();
driver()->scale(1);
fl_draw_image(img_background->array, 0, 0,
img_background->data_w(), img_background->data_h());
driver()->scale(s);
delete img_background;
// delete background offscreen
XFreePixmap(fl_display, shape_data_->background);
delete shape_data_->mask;
#endif // FLTK_USE_CAIRO
free(shape_data_);
shape_data_ = NULL;
}
Fl_RGB_Image *image = Fl::screen_driver()->read_win_rectangle(0, 0, width, height, 0); Fl_RGB_Image *image = Fl::screen_driver()->read_win_rectangle(0, 0, width, height, 0);
return image; return image;
} }
void Fl_Xlib_Image_Surface_Driver::end_current() void Fl_Xlib_Image_Surface_Driver::end_current()
{ {
fl_window = pre_window; fl_window = pre_window;
Fl_Surface_Device::end_current(); Fl_Surface_Device::end_current();
} }
#if FLTK_USE_CAIRO
void Fl_Xlib_Image_Surface_Driver::mask(const Fl_RGB_Image *mask) {
bool using_copy = false;
shape_data_ = (struct shape_data_type*)calloc(1, sizeof(struct shape_data_type));
int W, H;
cairo_t *c = ((Fl_Cairo_Graphics_Driver*)driver())->cr();
cairo_surface_t *c_surface = cairo_get_target(c);
W = cairo_xlib_surface_get_width(c_surface);
H = cairo_xlib_surface_get_height(c_surface);
if (W != mask->data_w() || H != mask->data_h()) {
Fl_RGB_Image *copy = (Fl_RGB_Image*)mask->copy(W, H);
mask = copy;
using_copy = true;
}
shape_data_->mask_pattern_ = Fl_Cairo_Graphics_Driver::calc_cairo_mask(mask);
//duplicate current offscreen content to new cairo_t* shape_data_->bg_cr
int width, height;
printable_rect(&width, &height);
Pixmap pxm = XCreatePixmap(fl_display, RootWindow(fl_display, fl_screen), W, H, fl_visual->depth);
cairo_surface_t *background = cairo_xlib_surface_create(fl_display, pxm, fl_visual->visual, W, H);
shape_data_->bg_cr = cairo_create(background);
cairo_surface_destroy(background);
cairo_surface_flush(c_surface);
cairo_pattern_t *pat = cairo_pattern_create_for_surface(c_surface);
cairo_set_source(shape_data_->bg_cr, pat),
cairo_paint(shape_data_->bg_cr);
cairo_pattern_destroy(pat);
shape_data_->scale = double(width) / W;
if (using_copy) delete mask;
}
#else
void Fl_Xlib_Image_Surface_Driver::mask(const Fl_RGB_Image *mask) {
shape_data_ = (struct shape_data_type*)calloc(1, sizeof(struct shape_data_type));
// get dimensions
int W, H;
Fl::screen_driver()->offscreen_size(offscreen, W, H);
// compute depth-1 mask
shape_data_->mask = Fl_Image_Surface_Driver::RGB3_to_RGB1(mask, W, H);
// duplicate current offscreen content to new, background offscreen
shape_data_->background = XCreatePixmap(fl_display, RootWindow(fl_display, fl_screen), W, H, fl_visual->depth);
driver()->restore_clip();
XCopyArea(fl_display, (Pixmap)offscreen, shape_data_->background, (GC)driver()->gc(), 0, 0, W, H, 0, 0);
}
#endif // FLTK_USE_CAIRO

View File

@ -505,7 +505,7 @@ void copy(Fl_Widget *, void *data) {
} }
return; return;
} }
if (strcmp(operation, "Fl_Copy_Surface") == 0) { if (strcmp(operation, "Fl_Copy_Surface") == 0) {
Fl_Copy_Surface *copy_surf; Fl_Copy_Surface *copy_surf;
if (target->as_window() && !target->parent()) { if (target->as_window() && !target->parent()) {
@ -521,8 +521,8 @@ void copy(Fl_Widget *, void *data) {
} }
delete copy_surf; delete copy_surf;
Fl_Surface_Device::pop_current(); Fl_Surface_Device::pop_current();
} }
if (strcmp(operation, "Fl_Printer") == 0 || strcmp(operation, "Fl_PostScript_File_Device") == 0) { if (strcmp(operation, "Fl_Printer") == 0 || strcmp(operation, "Fl_PostScript_File_Device") == 0) {
Fl_Paged_Device *p; Fl_Paged_Device *p;
int err; int err;
@ -549,7 +549,7 @@ void copy(Fl_Widget *, void *data) {
} else if (err > 1 && err_message) {fl_alert("%s", err_message); delete[] err_message;} } else if (err > 1 && err_message) {fl_alert("%s", err_message); delete[] err_message;}
delete p; delete p;
} }
if (strcmp(operation, "Fl_EPS_File_Surface") == 0) { if (strcmp(operation, "Fl_EPS_File_Surface") == 0) {
Fl_Native_File_Chooser fnfc; Fl_Native_File_Chooser fnfc;
fnfc.title("Save a .eps file"); fnfc.title("Save a .eps file");
@ -575,7 +575,7 @@ void copy(Fl_Widget *, void *data) {
} }
} }
} }
if (strcmp(operation, "Fl_SVG_File_Surface") == 0) { if (strcmp(operation, "Fl_SVG_File_Surface") == 0) {
Fl_Native_File_Chooser fnfc; Fl_Native_File_Chooser fnfc;
fnfc.title("Save a .svg file"); fnfc.title("Save a .svg file");
@ -602,7 +602,7 @@ void copy(Fl_Widget *, void *data) {
} }
} }
} }
if (strcmp(operation, "fl_capture_window()") == 0) { if (strcmp(operation, "fl_capture_window()") == 0) {
Fl_Window *win = target->as_window() ? target->as_window() : target->window(); Fl_Window *win = target->as_window() ? target->as_window() : target->window();
int X = target->as_window() ? 0 : target->x(); int X = target->as_window() ? 0 : target->x();
@ -618,6 +618,42 @@ void copy(Fl_Widget *, void *data) {
g2->show(); g2->show();
} }
} }
if (strcmp(operation, "Fl_Image_Surface::mask()") == 0) {
Fl_Image_Surface *surf = new Fl_Image_Surface(target->w(), target->h(), 1);
Fl_Surface_Device::push_current(surf);
fl_color(FL_BLACK);
fl_rectf(0, 0, target->w(), target->h());
fl_color(FL_WHITE);
fl_pie(0, 0, target->w(), target->h(), 0, 360);
if (target->top_window() == target) {
fl_color(FL_BLACK);
int mini = (target->w() < target->h() ? target->w() : target->h()) * 0.66;
fl_pie(target->w()/2 - mini/2, target->h()/2 - mini/2, mini, mini, 0, 360);
fl_color(FL_WHITE);
fl_font(FL_TIMES_BOLD, 120);
int dx, dy, l, h;
fl_text_extents("FLTK", dx, dy, l, h);
fl_draw("FLTK", target->w()/2 - l/2, target->h()/2 + h/2);
}
Fl_RGB_Image *mask = surf->image();
fl_color(FL_YELLOW);
fl_rectf(0, 0, target->w(), target->h());
Fl_Surface_Device::pop_current();
surf->mask(mask);
delete mask;
Fl_Surface_Device::push_current(surf);
surf->draw(target, 0, 0);
mask = surf->image();
Fl_Surface_Device::pop_current();
delete surf;
Fl_Window *win = new Fl_Window(mask->w(), mask->h(), operation);
Fl_Box *box = new Fl_Box(0, 0, mask->w(), mask->h());
box->bind_image(mask);
win->end();
win->show();
}
} }
class My_Button:public Fl_Button { class My_Button:public Fl_Button {
@ -650,7 +686,7 @@ void operation_cb(Fl_Widget* wid, void *data)
int main(int argc, char ** argv) { int main(int argc, char ** argv) {
Fl::scheme("plastic"); //Fl::scheme("plastic");
Fl_Window * w2 = new Fl_Window(500,568,"Graphics test"); Fl_Window * w2 = new Fl_Window(500,568,"Graphics test");
@ -730,6 +766,7 @@ int main(int argc, char ** argv) {
rb = new Fl_Radio_Round_Button(5,30,150,12, "Fl_EPS_File_Surface"); rb->callback(operation_cb, NULL); rb->labelsize(12); rb = new Fl_Radio_Round_Button(5,30,150,12, "Fl_EPS_File_Surface"); rb->callback(operation_cb, NULL); rb->labelsize(12);
rb = new Fl_Radio_Round_Button(170,30,150,12, "Fl_SVG_File_Surface"); rb->callback(operation_cb, NULL); rb->labelsize(12); rb = new Fl_Radio_Round_Button(170,30,150,12, "Fl_SVG_File_Surface"); rb->callback(operation_cb, NULL); rb->labelsize(12);
rb = new Fl_Radio_Round_Button(5,43,150,12, "fl_capture_window()"); rb->callback(operation_cb, NULL); rb->labelsize(12); rb = new Fl_Radio_Round_Button(5,43,150,12, "fl_capture_window()"); rb->callback(operation_cb, NULL); rb->labelsize(12);
rb = new Fl_Radio_Round_Button(170,43,150,12, "Fl_Image_Surface::mask()"); rb->callback(operation_cb, NULL); rb->labelsize(12);
g1->end(); g1->end();
Fl_Group *g2 = new Fl_Group(0,0,w3->w(),w3->h()); Fl_Group *g2 = new Fl_Group(0,0,w3->w(),w3->h());