mirror of https://github.com/fltk/fltk
New member function Fl_Image_Surface::mask(Fl_RGB_Image*)
This commit is contained in:
parent
b9ac6bd728
commit
3e61ec7044
|
@ -82,6 +82,7 @@ public:
|
|||
int printable_rect(int *w, int *h) FL_OVERRIDE;
|
||||
Fl_Offscreen offscreen();
|
||||
void rescale();
|
||||
void mask(const Fl_RGB_Image *);
|
||||
};
|
||||
|
||||
|
||||
|
@ -107,11 +108,15 @@ protected:
|
|||
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);}
|
||||
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 translate(int x, int y) FL_OVERRIDE = 0;
|
||||
void untranslate() FL_OVERRIDE = 0;
|
||||
int printable_rect(int *w, int *h) FL_OVERRIDE;
|
||||
virtual Fl_RGB_Image *image() = 0;
|
||||
virtual void mask(const Fl_RGB_Image *) {}
|
||||
/** Each platform implements this function its own way.
|
||||
It returns an object implementing all virtual functions
|
||||
of class Fl_Image_Surface_Driver for the plaform.
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 197 KiB |
|
@ -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.
|
||||
/** 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.
|
||||
|
||||
The returned object contains its own copy of the RGB data.
|
||||
The caller is responsible for deleting the image.
|
||||
\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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
@ -57,12 +66,22 @@ 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();
|
||||
pre_window = fl_window;
|
||||
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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -521,7 +521,7 @@ void copy(Fl_Widget *, void *data) {
|
|||
}
|
||||
delete copy_surf;
|
||||
Fl_Surface_Device::pop_current();
|
||||
}
|
||||
}
|
||||
|
||||
if (strcmp(operation, "Fl_Printer") == 0 || strcmp(operation, "Fl_PostScript_File_Device") == 0) {
|
||||
Fl_Paged_Device *p;
|
||||
|
@ -618,6 +618,42 @@ void copy(Fl_Widget *, void *data) {
|
|||
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 {
|
||||
|
@ -650,7 +686,7 @@ void operation_cb(Fl_Widget* wid, void *data)
|
|||
|
||||
int main(int argc, char ** argv) {
|
||||
|
||||
Fl::scheme("plastic");
|
||||
//Fl::scheme("plastic");
|
||||
|
||||
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(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(170,43,150,12, "Fl_Image_Surface::mask()"); rb->callback(operation_cb, NULL); rb->labelsize(12);
|
||||
g1->end();
|
||||
|
||||
Fl_Group *g2 = new Fl_Group(0,0,w3->w(),w3->h());
|
||||
|
|
Loading…
Reference in New Issue