From c91e48149beda4538d47e5a3a3c393f7690d2aa4 Mon Sep 17 00:00:00 2001 From: Lauri Kasanen Date: Mon, 16 Mar 2015 18:12:28 +0000 Subject: Add support for accelerated alpha blending under X11. git-svn-id: file:///fltk/svn/fltk/branches/branch-1.3@10628 ea41ed52-d2ee-0310-a9c1-e6b18d33e121 --- CHANGES | 14 ++++++------ FL/Fl_Device.H | 3 +++ FL/x.H | 1 + src/Fl_Double_Window.cxx | 34 ++++++++++++++++++++++++++++-- src/Fl_Image.cxx | 15 ++++++++++--- src/fl_draw_image.cxx | 55 ++++++++++++++++++++++++++++++++++++++++++------ 6 files changed, 105 insertions(+), 17 deletions(-) diff --git a/CHANGES b/CHANGES index 049b2db8c..2ce0477c8 100644 --- a/CHANGES +++ b/CHANGES @@ -5,10 +5,10 @@ CHANGES IN FLTK 1.3.4 RELEASED: ??? ?? ???? - Added full support of true subwindows to the Mac OS X platform. Window nesting to any depth is possible. An Fl_Gl_Window window or subwindow can contain Fl_Window's as subwindows. - - Added the Fl_Shared_Image::scale(width, height) function which gives - a shared image its own drawing size, independently of the size of the - underlying image. This improves much image drawing on high resolution - surfaces such as Laser printers, PDF files, or Apple retina displays. + - Added the Fl_Shared_Image::scale(width, height) function which gives + a shared image its own drawing size, independently of the size of the + underlying image. This improves much image drawing on high resolution + surfaces such as Laser printers, PDF files, or Apple retina displays. Other improvements @@ -23,8 +23,10 @@ CHANGES IN FLTK 1.3.4 RELEASED: ??? ?? ???? (i.e. when called) - only destruction is delayed as before. - The PostScript code output when printing images under Linux/Unix is quite smaller due to use of lossless compression techniques. - - The Linux/Unix printer dialog now uses BSD-style printing commands - (lpr/lpq) when SystemV-style commands (lp/lpstat) are not available. + - The Linux/Unix printer dialog now uses BSD-style printing commands + (lpr/lpq) when SystemV-style commands (lp/lpstat) are not available. + - Drawing alpha-blended images under X11 is now accelerated with + Xrender. Bug fixes diff --git a/FL/Fl_Device.H b/FL/Fl_Device.H index 4d048d179..df66e7a75 100644 --- a/FL/Fl_Device.H +++ b/FL/Fl_Device.H @@ -525,6 +525,9 @@ public: int height(); int descent(); void copy_offscreen(int x, int y, int w, int h, Fl_Offscreen pixmap, int srcx, int srcy); +#if ! defined(FL_DOXYGEN) + void copy_offscreen_with_alpha(int x, int y, int w, int h, Fl_Offscreen pixmap, int srcx, int srcy); +#endif }; #endif diff --git a/FL/x.H b/FL/x.H index f1f23f918..336acff9e 100644 --- a/FL/x.H +++ b/FL/x.H @@ -84,6 +84,7 @@ extern FL_EXPORT ulong fl_event_time; // off-screen pixmaps: create, destroy, draw into, copy to window: typedef ulong Fl_Offscreen; # define fl_create_offscreen(w,h) XCreatePixmap(fl_display, RootWindow(fl_display, fl_screen), w, h, fl_visual->depth) +# define fl_create_offscreen_with_alpha(w,h) XCreatePixmap(fl_display, RootWindow(fl_display, fl_screen), w, h, 32) // begin/end are macros that save the old state in local variables: # define fl_begin_offscreen(pixmap) \ Window _sw=fl_window; fl_window=pixmap; \ diff --git a/src/Fl_Double_Window.cxx b/src/Fl_Double_Window.cxx index 99ba3e7d1..f85af930b 100644 --- a/src/Fl_Double_Window.cxx +++ b/src/Fl_Double_Window.cxx @@ -129,14 +129,44 @@ void Fl_Graphics_Driver::copy_offscreen(int x, int y, int w, int h, Fl_Offscreen #if defined(USE_X11) +#ifdef HAVE_XRENDER +#include +#endif + void Fl_Xlib_Graphics_Driver::copy_offscreen(int x, int y, int w, int h, Fl_Offscreen pixmap, int srcx, int srcy) { XCopyArea(fl_display, pixmap, fl_window, fl_gc, srcx, srcy, w, h, x, y); } +void Fl_Xlib_Graphics_Driver::copy_offscreen_with_alpha(int x, int y, int w, int h, + Fl_Offscreen pixmap, int srcx, int srcy) { +#ifdef HAVE_XRENDER + XRenderPictureAttributes srcattr; + memset(&srcattr, 0, sizeof(XRenderPictureAttributes)); + XRenderPictFormat *srcfmt = XRenderFindStandardFormat(fl_display, PictStandardARGB32); + XRenderPictFormat *dstfmt = XRenderFindStandardFormat(fl_display, PictStandardRGB24); + + Picture src = XRenderCreatePicture(fl_display, pixmap, srcfmt, 0, &srcattr); + Picture dst = XRenderCreatePicture(fl_display, fl_window, dstfmt, 0, &srcattr); + + if (!src || !dst) { + fprintf(stderr, "Failed to create Render pictures (%lu %lu)\n", src, dst); + return; + } + + const Fl_Region clipr = fl_clip_region(); + if (clipr) + XRenderSetPictureClipRegion(fl_display, dst, clipr); + + XRenderComposite(fl_display, PictOpOver, src, None, dst, srcx, srcy, 0, 0, + x, y, w, h); + + XRenderFreePicture(fl_display, src); + XRenderFreePicture(fl_display, dst); +#endif +} -// maybe someone feels inclined to implement alpha blending on X11? char fl_can_do_alpha_blending() { - return 0; + return Fl_X::xrender_supported(); } #elif defined(WIN32) diff --git a/src/Fl_Image.cxx b/src/Fl_Image.cxx index 340b1bcd4..4dea195db 100644 --- a/src/Fl_Image.cxx +++ b/src/Fl_Image.cxx @@ -711,6 +711,12 @@ void Fl_Xlib_Graphics_Driver::draw(Fl_RGB_Image *img, int XP, int YP, int WP, in fl_begin_offscreen((Fl_Offscreen)img->id_); fl_draw_image(img->array, 0, 0, img->w(), img->h(), img->d(), img->ld()); fl_end_offscreen(); + } else if (img->d() == 4 && fl_can_do_alpha_blending()) { + img->id_ = fl_create_offscreen_with_alpha(img->w(), img->h()); + fl_begin_offscreen((Fl_Offscreen)img->id_); + fl_draw_image(img->array, 0, 0, img->w(), img->h(), img->d() | FL_IMAGE_WITH_ALPHA, + img->ld()); + fl_end_offscreen(); } } if (img->id_) { @@ -726,9 +732,12 @@ void Fl_Xlib_Graphics_Driver::draw(Fl_RGB_Image *img, int XP, int YP, int WP, in int oy = Y-cy; if (oy < 0) oy += img->h(); XSetClipOrigin(fl_display, fl_gc, X-cx, Y-cy); } - - copy_offscreen(X, Y, W, H, img->id_, cx, cy); - + + if (img->d() == 4 && fl_can_do_alpha_blending()) + copy_offscreen_with_alpha(X, Y, W, H, img->id_, cx, cy); + else + copy_offscreen(X, Y, W, H, img->id_, cx, cy); + if (img->mask_) { // put the old clip region back XSetClipOrigin(fl_display, fl_gc, 0, 0); diff --git a/src/fl_draw_image.cxx b/src/fl_draw_image.cxx index 68ff5505d..2562e473e 100644 --- a/src/fl_draw_image.cxx +++ b/src/fl_draw_image.cxx @@ -316,6 +316,13 @@ static void xrgb_converter(const uchar *from, uchar *to, int w, int delta) { INNARDS32((from[0]<<16)+(from[1]<<8)+(from[2])); } +static void argb_premul_converter(const uchar *from, uchar *to, int w, int delta) { + INNARDS32((from[3] << 24) + + (((from[0] * from[3]) / 255) << 16) + + (((from[1] * from[3]) / 255) << 8) + + ((from[2] * from[3]) / 255)); +} + static void bgrx_converter(const uchar *from, uchar *to, int w, int delta) { INNARDS32((from[0]<<8)+(from[1]<<16)+(unsigned(from[2])<<24)); } @@ -451,7 +458,8 @@ static void figure_out_visual() { static void innards(const uchar *buf, int X, int Y, int W, int H, int delta, int linedelta, int mono, - Fl_Draw_Image_Cb cb, void* userdata) + Fl_Draw_Image_Cb cb, void* userdata, + const bool alpha) { if (!linedelta) linedelta = W*delta; @@ -462,11 +470,28 @@ static void innards(const uchar *buf, int X, int Y, int W, int H, dy -= Y; if (!bytes_per_pixel) figure_out_visual(); + const unsigned oldbpp = bytes_per_pixel; + const GC oldgc = fl_gc; + static GC gc32 = None; xi.width = w; xi.height = h; void (*conv)(const uchar *from, uchar *to, int w, int delta) = converter; if (mono) conv = mono_converter; + if (alpha) { + // This flag states the destination format is ARGB32 (big-endian), pre-multiplied. + bytes_per_pixel = 4; + conv = argb_premul_converter; + xi.depth = 32; + xi.bits_per_pixel = 32; + + // Do we need a new GC? + if (fl_visual->depth != 32) { + if (gc32 == None) + gc32 = XCreateGC(fl_display, fl_window, 0, NULL); + fl_gc = gc32; + } + } // See if the data is already in the right format. Unfortunately // some 32-bit x servers (XFree86) care about the unknown 8 bits @@ -534,21 +559,39 @@ static void innards(const uchar *buf, int X, int Y, int W, int H, delete[] linebuf; } } + + if (alpha) { + bytes_per_pixel = oldbpp; + xi.depth = fl_visual->depth; + xi.bits_per_pixel = oldbpp * 8; + + if (fl_visual->depth != 32) { + fl_gc = oldgc; + } + } } void Fl_Xlib_Graphics_Driver::draw_image(const uchar* buf, int x, int y, int w, int h, int d, int l){ - innards(buf,x,y,w,h,d,l,(d<3&&d>-3),0,0); + + const bool alpha = !!(d & FL_IMAGE_WITH_ALPHA); + d &= ~FL_IMAGE_WITH_ALPHA; + + innards(buf,x,y,w,h,d,l,(d<3&&d>-3),0,0,alpha); } void Fl_Xlib_Graphics_Driver::draw_image(Fl_Draw_Image_Cb cb, void* data, int x, int y, int w, int h,int d) { - innards(0,x,y,w,h,d,0,(d<3&&d>-3),cb,data); + + const bool alpha = !!(d & FL_IMAGE_WITH_ALPHA); + d &= ~FL_IMAGE_WITH_ALPHA; + + innards(0,x,y,w,h,d,0,(d<3&&d>-3),cb,data,alpha); } void Fl_Xlib_Graphics_Driver::draw_image_mono(const uchar* buf, int x, int y, int w, int h, int d, int l){ - innards(buf,x,y,w,h,d,l,1,0,0); + innards(buf,x,y,w,h,d,l,1,0,0,0); } void Fl_Xlib_Graphics_Driver::draw_image_mono(Fl_Draw_Image_Cb cb, void* data, int x, int y, int w, int h,int d) { - innards(0,x,y,w,h,d,0,1,cb,data); + innards(0,x,y,w,h,d,0,1,cb,data,0); } void fl_rectf(int x, int y, int w, int h, uchar r, uchar g, uchar b) { @@ -558,7 +601,7 @@ void fl_rectf(int x, int y, int w, int h, uchar r, uchar g, uchar b) { } else { uchar c[3]; c[0] = r; c[1] = g; c[2] = b; - innards(c,x,y,w,h,0,0,0,0,0); + innards(c,x,y,w,h,0,0,0,0,0,0); } } -- cgit v1.2.3