summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorManoloFLTK <41016272+ManoloFLTK@users.noreply.github.com>2023-11-20 17:13:09 +0100
committerManoloFLTK <41016272+ManoloFLTK@users.noreply.github.com>2023-12-04 12:34:44 +0100
commit3e61ec7044942ebaeda948c64df762a0250f8954 (patch)
tree642c08a6f7b120c2237f5e457074670d0ccf3e86 /src
parentb9ac6bd728659a7780d7cde024f34607601e8a7f (diff)
New member function Fl_Image_Surface::mask(Fl_RGB_Image*)
Diffstat (limited to 'src')
-rw-r--r--src/Fl_Image_Surface.cxx94
-rw-r--r--src/drivers/Cairo/Fl_Cairo_Graphics_Driver.H1
-rw-r--r--src/drivers/Cairo/Fl_Cairo_Graphics_Driver.cxx37
-rw-r--r--src/drivers/GDI/Fl_GDI_Graphics_Driver.H1
-rw-r--r--src/drivers/GDI/Fl_GDI_Image_Surface_Driver.H6
-rw-r--r--src/drivers/GDI/Fl_GDI_Image_Surface_Driver.cxx86
-rw-r--r--src/drivers/Quartz/Fl_Quartz_Image_Surface_Driver.H5
-rw-r--r--src/drivers/Quartz/Fl_Quartz_Image_Surface_Driver.cxx76
-rw-r--r--src/drivers/Wayland/Fl_Wayland_Image_Surface_Driver.H6
-rw-r--r--src/drivers/Wayland/Fl_Wayland_Image_Surface_Driver.cxx75
-rw-r--r--src/drivers/Xlib/Fl_Xlib_Image_Surface_Driver.H13
-rw-r--r--src/drivers/Xlib/Fl_Xlib_Image_Surface_Driver.cxx122
12 files changed, 515 insertions, 7 deletions
diff --git a/src/Fl_Image_Surface.cxx b/src/Fl_Image_Surface.cxx
index 29c5bf145..739d38c44 100644
--- a/src/Fl_Image_Surface.cxx
+++ b/src/Fl_Image_Surface.cxx
@@ -90,15 +90,63 @@ int Fl_Image_Surface_Driver::printable_rect(int *w, int *h) {
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
*/
-/** 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 caller is responsible for deleting the image.
+/** 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 caller is responsible for deleting it.
+
+ \see Fl_Image_Surface::mask(Fl_RGB_Image*)
*/
Fl_RGB_Image *Fl_Image_Surface::image() {
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
static Fl_Image_Surface **offscreen_api_surface = NULL;
diff --git a/src/drivers/Cairo/Fl_Cairo_Graphics_Driver.H b/src/drivers/Cairo/Fl_Cairo_Graphics_Driver.H
index 8677a920f..e55f051d7 100644
--- a/src/drivers/Cairo/Fl_Cairo_Graphics_Driver.H
+++ b/src/drivers/Cairo/Fl_Cairo_Graphics_Driver.H
@@ -68,6 +68,7 @@ public:
cairo_t *cr() { return cairo_; }
PangoLayout *pango_layout() {return pango_layout_;}
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);
diff --git a/src/drivers/Cairo/Fl_Cairo_Graphics_Driver.cxx b/src/drivers/Cairo/Fl_Cairo_Graphics_Driver.cxx
index cda328c17..40264c757 100644
--- a/src/drivers/Cairo/Fl_Cairo_Graphics_Driver.cxx
+++ b/src/drivers/Cairo/Fl_Cairo_Graphics_Driver.cxx
@@ -1517,4 +1517,41 @@ void Fl_Cairo_Graphics_Driver::focus_rect(int x, int y, int w, int h)
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
diff --git a/src/drivers/GDI/Fl_GDI_Graphics_Driver.H b/src/drivers/GDI/Fl_GDI_Graphics_Driver.H
index 4d1f45c82..03371c8c4 100644
--- a/src/drivers/GDI/Fl_GDI_Graphics_Driver.H
+++ b/src/drivers/GDI/Fl_GDI_Graphics_Driver.H
@@ -70,6 +70,7 @@ public:
// --- bitmap stuff
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;
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;
diff --git a/src/drivers/GDI/Fl_GDI_Image_Surface_Driver.H b/src/drivers/GDI/Fl_GDI_Image_Surface_Driver.H
index d44d57cd6..129a4ecbc 100644
--- a/src/drivers/GDI/Fl_GDI_Image_Surface_Driver.H
+++ b/src/drivers/GDI/Fl_GDI_Image_Surface_Driver.H
@@ -26,6 +26,12 @@ class Fl_GDI_Image_Surface_Driver : public Fl_Image_Surface_Driver {
public:
HWND pre_window;
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();
void set_current() FL_OVERRIDE;
diff --git a/src/drivers/GDI/Fl_GDI_Image_Surface_Driver.cxx b/src/drivers/GDI/Fl_GDI_Image_Surface_Driver.cxx
index 5a4d5ada7..7e1c2f24e 100644
--- a/src/drivers/GDI/Fl_GDI_Image_Surface_Driver.cxx
+++ b/src/drivers/GDI/Fl_GDI_Image_Surface_Driver.cxx
@@ -19,6 +19,7 @@
#include "../WinAPI/Fl_WinAPI_Screen_Driver.H"
#include "Fl_GDI_Image_Surface_Driver.H"
#include <FL/platform.H>
+#include <FL/Fl_Bitmap.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());
if (d != 1 && high_res) ((Fl_GDI_Graphics_Driver*)driver())->scale(d);
origin.x = origin.y = 0;
+ shape_data_ = NULL;
}
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);
delete driver();
}
@@ -67,6 +74,43 @@ void Fl_GDI_Image_Surface_Driver::untranslate() {
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);
return image;
}
@@ -81,3 +125,45 @@ void Fl_GDI_Image_Surface_Driver::end_current()
fl_window = pre_window;
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);
+}
+
diff --git a/src/drivers/Quartz/Fl_Quartz_Image_Surface_Driver.H b/src/drivers/Quartz/Fl_Quartz_Image_Surface_Driver.H
index b6f58ec8f..69e00dfd0 100644
--- a/src/drivers/Quartz/Fl_Quartz_Image_Surface_Driver.H
+++ b/src/drivers/Quartz/Fl_Quartz_Image_Surface_Driver.H
@@ -21,6 +21,11 @@
#include <FL/platform.H>
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;
public:
FLWindow *pre_window;
diff --git a/src/drivers/Quartz/Fl_Quartz_Image_Surface_Driver.cxx b/src/drivers/Quartz/Fl_Quartz_Image_Surface_Driver.cxx
index 451078484..fb522745a 100644
--- a/src/drivers/Quartz/Fl_Quartz_Image_Surface_Driver.cxx
+++ b/src/drivers/Quartz/Fl_Quartz_Image_Surface_Driver.cxx
@@ -1,7 +1,7 @@
//
// 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
// 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) {
+# if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
+ mask_ = NULL;
+#endif
int W = w, H = h;
float s = 1;
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() {
+# 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) {
void *data = CGBitmapContextGetData((CGContextRef)offscreen);
free(data);
@@ -56,6 +65,7 @@ Fl_Quartz_Image_Surface_Driver::~Fl_Quartz_Image_Surface_Driver() {
}
delete driver();
}
+
void Fl_Quartz_Image_Surface_Driver::set_current() {
Fl_Surface_Device::set_current();
@@ -63,6 +73,15 @@ void Fl_Quartz_Image_Surface_Driver::set_current() {
driver()->gc((CGContextRef)offscreen);
fl_window = 0;
((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) {
@@ -79,6 +98,13 @@ void Fl_Quartz_Image_Surface_Driver::untranslate() {
Fl_RGB_Image* Fl_Quartz_Image_Surface_Driver::image()
{
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 H = (int)CGBitmapContextGetHeight((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()
{
+# 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_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
diff --git a/src/drivers/Wayland/Fl_Wayland_Image_Surface_Driver.H b/src/drivers/Wayland/Fl_Wayland_Image_Surface_Driver.H
index 285222ced..ae32ac3df 100644
--- a/src/drivers/Wayland/Fl_Wayland_Image_Surface_Driver.H
+++ b/src/drivers/Wayland/Fl_Wayland_Image_Surface_Driver.H
@@ -25,6 +25,12 @@ class Fl_Wayland_Image_Surface_Driver : public Fl_Image_Surface_Driver {
public:
Fl_Wayland_Image_Surface_Driver(int w, int h, int high_res, Fl_Offscreen off);
~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 translate(int x, int y) FL_OVERRIDE;
void untranslate() FL_OVERRIDE;
diff --git a/src/drivers/Wayland/Fl_Wayland_Image_Surface_Driver.cxx b/src/drivers/Wayland/Fl_Wayland_Image_Surface_Driver.cxx
index 83419e5b2..f25fe025d 100644
--- a/src/drivers/Wayland/Fl_Wayland_Image_Surface_Driver.cxx
+++ b/src/drivers/Wayland/Fl_Wayland_Image_Surface_Driver.cxx
@@ -22,6 +22,7 @@
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) {
+ shape_data_ = NULL;
float s = 1;
int d = 1;
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() {
+ 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) {
struct Fl_Wayland_Graphics_Driver::draw_buffer *buffer =
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() {
+ 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
struct Fl_Wayland_Graphics_Driver::draw_buffer *off_buf =
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 *p = rgb;
uchar *q;
@@ -111,3 +153,34 @@ Fl_RGB_Image* Fl_Wayland_Image_Surface_Driver::image() {
image->alloc_array = 1;
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;
+}
diff --git a/src/drivers/Xlib/Fl_Xlib_Image_Surface_Driver.H b/src/drivers/Xlib/Fl_Xlib_Image_Surface_Driver.H
index ab702840d..382daf886 100644
--- a/src/drivers/Xlib/Fl_Xlib_Image_Surface_Driver.H
+++ b/src/drivers/Xlib/Fl_Xlib_Image_Surface_Driver.H
@@ -1,7 +1,7 @@
//
// 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
// 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 untranslate() FL_OVERRIDE;
Fl_RGB_Image *image() FL_OVERRIDE;
+ void mask(const Fl_RGB_Image *) FL_OVERRIDE;
#if FLTK_USE_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
};
diff --git a/src/drivers/Xlib/Fl_Xlib_Image_Surface_Driver.cxx b/src/drivers/Xlib/Fl_Xlib_Image_Surface_Driver.cxx
index bcaf7d46a..87e55b417 100644
--- a/src/drivers/Xlib/Fl_Xlib_Image_Surface_Driver.cxx
+++ b/src/drivers/Xlib/Fl_Xlib_Image_Surface_Driver.cxx
@@ -17,6 +17,7 @@
#include <FL/platform.H>
#include "Fl_Xlib_Image_Surface_Driver.H"
#include "../../Fl_Screen_Driver.H"
+#include <stdlib.h>
#if FLTK_USE_CAIRO
# include <cairo-xlib.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);
}
+ shape_data_ = NULL;
#if FLTK_USE_CAIRO
driver(new Fl_X11_Cairo_Graphics_Driver());
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() {
#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_);
+#else
+ if (shape_data_) {
+ XFreePixmap(fl_display, shape_data_->background);
+ delete shape_data_->mask;
+ free(shape_data_);
+ }
#endif
if (offscreen && !external_offscreen) XFreePixmap(fl_display, (Pixmap)offscreen);
delete driver();
@@ -84,14 +103,117 @@ void Fl_Xlib_Image_Surface_Driver::untranslate() {
#endif
}
+
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);
return image;
}
+
void Fl_Xlib_Image_Surface_Driver::end_current()
{
fl_window = pre_window;
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