diff options
| author | Manolo Gouy <Manolo> | 2014-11-06 16:48:57 +0000 |
|---|---|---|
| committer | Manolo Gouy <Manolo> | 2014-11-06 16:48:57 +0000 |
| commit | bd1446a6eb5b845179bb850c0f371a323141e8b0 (patch) | |
| tree | b1dd6983fc41879881452d9e91f89e2fadb636f5 /src | |
| parent | f5e023f9c8a9b328b623804f67bbcb104dc7b6ff (diff) | |
Fix for STR#3142 where fl_read_image() correctly reads GL data under X11, but ignores them under MSWindows and Mac OS X.
Moreover, fl_read_image() behaves differently with and without OS virtualization for X11 and MSWindows.
The patched function reads whatever is in the rectangle transmitted in arguments, with and without GL data, with and without subwindows,
on ‘true’ OS or on virtualized OS.
git-svn-id: file:///fltk/svn/fltk/branches/branch-1.3@10436 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
Diffstat (limited to 'src')
| -rw-r--r-- | src/Fl_Gl_Device_Plugin.cxx | 97 | ||||
| -rw-r--r-- | src/Fl_Paged_Device.cxx | 9 | ||||
| -rw-r--r-- | src/Fl_cocoa.mm | 213 | ||||
| -rw-r--r-- | src/Fl_win32.cxx | 5 | ||||
| -rw-r--r-- | src/fl_read_image.cxx | 165 | ||||
| -rw-r--r-- | src/fl_read_image_win32.cxx | 10 |
6 files changed, 387 insertions, 112 deletions
diff --git a/src/Fl_Gl_Device_Plugin.cxx b/src/Fl_Gl_Device_Plugin.cxx index fbcb2f613..ed2889dbe 100644 --- a/src/Fl_Gl_Device_Plugin.cxx +++ b/src/Fl_Gl_Device_Plugin.cxx @@ -3,7 +3,7 @@ // // implementation of class Fl_Gl_Device_Plugin for the Fast Light Tool Kit (FLTK). // -// Copyright 2010 by Bill Spitzak and others. +// Copyright 2010-2014 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 @@ -20,19 +20,36 @@ #include <FL/Fl_Printer.H> #include <FL/Fl_Gl_Window.H> #include "Fl_Gl_Choice.H" +#include <FL/Fl_RGB_Image.H> #include "FL/Fl.H" -#ifndef __APPLE__ -#include "FL/fl_draw.H" -#endif #if defined(__APPLE__) -static void imgProviderReleaseData (void *info, const void *data, size_t size) +uchar *convert_BGRA_to_RGB(uchar *baseAddress, int w, int h, int mByteWidth) { - free((void *)data); + uchar *newimg = new uchar[3*w*h]; + uchar *to = newimg; + for (int i = 0; i < h; i++) { + uchar *from = baseAddress + i * mByteWidth; + for (int j = 0; j < w; j++, from += 4) { +#if __ppc__ + memcpy(to, from + 1, 3); + to += 3; +#else + *(to++) = *(from+2); + *(to++) = *(from+1); + *(to++) = *from; +#endif + } + } + delete[] baseAddress; + return newimg; } #endif -static void print_gl_window(Fl_Gl_Window *glw, int x, int y, int height) +static Fl_RGB_Image* capture_gl_rectangle(Fl_Gl_Window *glw, int x, int y, int w, int h) +/* captures a rectangle of a Fl_Gl_Window window, and returns it as a RGB image + stored from bottom to top. + */ { #if defined(__APPLE__) const int bytesperpixel = 4; @@ -48,66 +65,46 @@ static void print_gl_window(Fl_Gl_Window *glw, int x, int y, int height) glPixelStorei(GL_PACK_SKIP_ROWS, 0); glPixelStorei(GL_PACK_SKIP_PIXELS, 0); // Read a block of pixels from the frame buffer - int mByteWidth = glw->w() * bytesperpixel; + int mByteWidth = w * bytesperpixel; mByteWidth = (mByteWidth + 3) & ~3; // Align to 4 bytes - uchar *baseAddress = (uchar*)malloc(mByteWidth * glw->h()); - glReadPixels(0, 0, glw->w(), glw->h(), + uchar *baseAddress = new uchar[mByteWidth * h]; + glReadPixels(x, glw->h() - (y+h), w, h, #if defined(__APPLE__) - GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, + GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, #else - GL_RGB, GL_UNSIGNED_BYTE, + GL_RGB, GL_UNSIGNED_BYTE, #endif - baseAddress); + baseAddress); glPopClientAttrib(); #if defined(__APPLE__) -// kCGBitmapByteOrder32Host and CGBitmapInfo are supposed to arrive with 10.4 -// but some 10.4 don't have kCGBitmapByteOrder32Host, so we play a little #define game -#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_4 -#define kCGBitmapByteOrder32Host 0 -#define CGBitmapInfo CGImageAlphaInfo -#elif ! defined(kCGBitmapByteOrder32Host) -#ifdef __BIG_ENDIAN__ -#define kCGBitmapByteOrder32Host (4 << 12) -#else /* Little endian. */ -#define kCGBitmapByteOrder32Host (2 << 12) -#endif + baseAddress = convert_BGRA_to_RGB(baseAddress, w, h, mByteWidth); + mByteWidth = 3 * w; #endif - CGColorSpaceRef cSpace = CGColorSpaceCreateDeviceRGB(); - CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, baseAddress, mByteWidth * glw->h(), imgProviderReleaseData); - CGImageRef image = CGImageCreate(glw->w(), glw->h(), 8, 8*bytesperpixel, mByteWidth, cSpace, - (CGBitmapInfo)(kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host), - provider, NULL, false, kCGRenderingIntentDefault); - if(image == NULL) return; - CGContextSaveGState(fl_gc); - CGContextTranslateCTM(fl_gc, 0, height); - CGContextScaleCTM(fl_gc, 1.0f, -1.0f); - CGRect rect = { { x, height - y - glw->h() }, { glw->w(), glw->h() } }; - Fl_X::q_begin_image(rect, 0, 0, glw->w(), glw->h()); - CGContextDrawImage(fl_gc, rect, image); - Fl_X::q_end_image(); - CGContextRestoreGState(fl_gc); - CGImageRelease(image); - CGColorSpaceRelease(cSpace); - CGDataProviderRelease(provider); -#else - fl_draw_image(baseAddress + (glw->h() - 1) * mByteWidth, x, y , glw->w(), glw->h(), bytesperpixel, - mByteWidth); - free(baseAddress); -#endif // __APPLE__ + Fl_RGB_Image *img = new Fl_RGB_Image(baseAddress, w, h, 3, mByteWidth); + img->alloc_array = 1; + return img; } /** - This class will make sure that OpenGL printing is available if fltk_gl - was linked to the program. + This class will make sure that OpenGL printing/screen capture is available if fltk_gl + was linked to the program */ class Fl_Gl_Device_Plugin : public Fl_Device_Plugin { public: Fl_Gl_Device_Plugin() : Fl_Device_Plugin(name()) { } virtual const char *name() { return "opengl.device.fltk.org"; } - virtual int print(Fl_Widget *w, int x, int y, int height) { + virtual int print(Fl_Widget *w, int x, int y, int height /*useless*/) { Fl_Gl_Window *glw = w->as_gl_window(); if (!glw) return 0; - print_gl_window(glw, x, y, height); - return 1; + Fl_RGB_Image *img = capture_gl_rectangle(glw, 0, 0, glw->w(), glw->h()); + fl_draw_image(img->array + (glw->h() - 1) * img->ld(), x, y , glw->w(), glw->h(), 3, - img->ld()); + delete img; + return 1; + } + virtual Fl_RGB_Image* rectangle_capture(Fl_Widget *widget, int x, int y, int w, int h) { + Fl_Gl_Window *glw = widget->as_gl_window(); + if (!glw) return NULL; + return capture_gl_rectangle(glw, x, y, w, h); } }; diff --git a/src/Fl_Paged_Device.cxx b/src/Fl_Paged_Device.cxx index fcc18eab2..7f4d9d1a6 100644 --- a/src/Fl_Paged_Device.cxx +++ b/src/Fl_Paged_Device.cxx @@ -3,7 +3,7 @@ // // implementation of Fl_Paged_Device class for the Fast Light Tool Kit (FLTK). // -// Copyright 2010-2011 by Bill Spitzak and others. +// Copyright 2010-2014 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 @@ -63,12 +63,7 @@ void Fl_Paged_Device::print_widget(Fl_Widget* widget, int delta_x, int delta_y) Fl_Plugin_Manager pm("fltk:device"); Fl_Device_Plugin *pi = (Fl_Device_Plugin*)pm.plugin("opengl.device.fltk.org"); if (pi) { - int height = 0; -#ifdef __APPLE__ - int width; - this->printable_rect(&width, &height); -#endif - drawn_by_plugin = pi->print(widget, 0, 0, height); + drawn_by_plugin = pi->print(widget, 0, 0, 0); } } if (!drawn_by_plugin) { diff --git a/src/Fl_cocoa.mm b/src/Fl_cocoa.mm index 61e327c55..abb568cde 100644 --- a/src/Fl_cocoa.mm +++ b/src/Fl_cocoa.mm @@ -3837,29 +3837,176 @@ int Fl::dnd(void) return true; } -static NSBitmapImageRep* rect_to_NSBitmapImageRep(Fl_Window *win, int x, int y, int w, int h) -// the returned value is autoreleased +static void write_bitmap_inside(NSBitmapImageRep *to, int to_width, NSBitmapImageRep *from, + int to_x, int to_y) +/* Copies in bitmap "to" the bitmap "from" with its top-left angle at coordinates to_x, to_y + On retina displays both bitmaps have double width and height + to_width is the width in screen units of "to". On retina, its pixel width is twice that. + */ { - NSRect rect; - NSView *winview = nil; + int to_w = (int)[to pixelsWide]; // pixel width of "to" + int from_w = (int)[from pixelsWide]; // pixel width of "from" + int from_h = [from pixelsHigh]; // pixel height of "from" + int to_depth = [to samplesPerPixel], from_depth = [from samplesPerPixel]; + int depth = 0; + if (to_depth > from_depth) depth = from_depth; + else if (from_depth > to_depth) depth = to_depth; + float factor = to_w / (float)to_width; // scaling factor is 1 for classic displays and 2 for retina + to_x = factor*to_x; // transform offset from screen unit to pixels + to_y = factor*to_y; + // perform the copy + uchar *tobytes = [to bitmapData] + to_y * to_w * to_depth + to_x * to_depth; + uchar *frombytes = [from bitmapData]; + for (int i = 0; i < from_h; i++) { + if (depth == 0) memcpy(tobytes, frombytes, from_w * from_depth); + else { + for (int j = 0; j < from_w; j++) { + memcpy(tobytes + j * to_depth, frombytes + j * from_depth, depth); + } + } + tobytes += to_w * to_depth; + frombytes += from_w * from_depth; + } +} + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 +static CGImageRef GL_rect_to_CGImage(Fl_Window *win, int x, int y, int w, int h) +// to be used with Mac OS ≥ 10.6 to support retina displays +// captures a rectangle from a GL window and returns it as a CGImageRef +// with retina the image dimensions are 2*w,2*h +// win is really a Fl_Gl_Window* +{ + CGRect rect; while (win->window()) { x += win->x(); y += win->y(); win = win->window(); } - if ( through_drawRect ) { - CGFloat epsilon = 0; - if (fl_mac_os_version >= 100600) epsilon = 0.5; // STR #2887 - rect = NSMakeRect(x - epsilon, y - epsilon, w, h); + NSRect r = [fl_xid(win) frame]; + rect = CGRectMake(r.origin.x + x, r.origin.y + win->h() - (y + h), w, h); // rect is target rect in window coordinates + // convert r to global display coordinates + rect.origin.y = CGDisplayPixelsHigh(CGMainDisplayID()) - (rect.origin.y + rect.size.height); + uint32_t count; + CGDirectDisplayID win_display; + CGGetDisplaysWithPoint(rect.origin, 1, &win_display, &count); // find display containing the window + CGRect bounds = CGDisplayBounds(win_display); + rect.origin.x -= bounds.origin.x; // rect is now in local display coordinates + rect.origin.y -= bounds.origin.y; + return CGDisplayCreateImageForRect(win_display, rect); // Mac OS 10.6 +} +#endif + + +static void imgProviderReleaseData (void *info, const void *data, size_t size) +{ + delete (Fl_RGB_Image *)info; +} + + +CGImageRef GL_rect_to_CGImage_10_5(Fl_Window *win, int x, int y, int w, int h) +// captures a rectangle from a GL window and returns it as a CGImageRef +// used with Mac OS X 10.5 and before +// win is really a Fl_Gl_Window* +{ + Fl_Plugin_Manager pm("fltk:device"); + Fl_Device_Plugin *pi = (Fl_Device_Plugin*)pm.plugin("opengl.device.fltk.org"); + if (!pi) return NULL; + Fl_RGB_Image *img = pi->rectangle_capture(win, x, y, w, h); + CGColorSpaceRef cSpace = CGColorSpaceCreateDeviceRGB(); + CGDataProviderRef provider = CGDataProviderCreateWithData(img, img->array, img->ld() * h, imgProviderReleaseData); + CGImageRef image = CGImageCreate(img->w(), img->h(), 8, 24, img->ld(), cSpace, + (CGBitmapInfo)(kCGImageAlphaNone), + provider, NULL, false, kCGRenderingIntentDefault); + CGColorSpaceRelease(cSpace); + CGDataProviderRelease(provider); + if (image == NULL) delete img; + return image; +} + + +static NSBitmapImageRep* GL_rect_to_nsbitmap(Fl_Window *win, int x, int y, int w, int h) +// captures a rectangle from a GL window and returns it as an allocated NSBitmapImageRep +{ + CGImageRef image; + BOOL toflip = YES; +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 + if (fl_mac_os_version >= 100600) { + image = GL_rect_to_CGImage(win, x, y, w, h); // BGRA top to bottom + toflip = NO; + } + else +#endif + image = GL_rect_to_CGImage_10_5(win, x, y, w, h); // RGB bottom to top + if (!image) return nil; + w = CGImageGetWidth(image); + h = CGImageGetHeight(image); + // convert image to RGBA writing it to a bitmap context + Fl_Offscreen offscreen = fl_create_offscreen(w, h); + fl_begin_offscreen(offscreen); + if (toflip) { + CGContextTranslateCTM(fl_gc, 0, h); + CGContextScaleCTM(fl_gc, 1.0f, -1.0f); + } + CGRect rect = CGRectMake(0, 0, w, h); + Fl_X::q_begin_image(rect, 0, 0, w, h); + CGContextDrawImage(fl_gc, rect, image); + Fl_X::q_end_image(); + CGImageRelease(image); + fl_end_offscreen(); + NSBitmapImageRep* bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL pixelsWide:w pixelsHigh:h bitsPerSample:8 samplesPerPixel:4 hasAlpha:YES isPlanar:NO colorSpaceName:NSDeviceRGBColorSpace bytesPerRow:4*w bitsPerPixel:32]; + memcpy([bitmap bitmapData], CGBitmapContextGetData(offscreen), CGBitmapContextGetBytesPerRow(offscreen)*CGBitmapContextGetHeight(offscreen)); + fl_delete_offscreen(offscreen); + return bitmap; +} + +static NSBitmapImageRep* rect_to_NSBitmapImageRep(Fl_Window *win, int x, int y, int w, int h) +/* Captures a rectangle from a mapped window. + On retina displays, the resulting bitmap has 2 pixels per screen unit. + The returned value is to be released after use + */ +{ + NSBitmapImageRep *bitmap = nil; + NSRect rect; + if (win->as_gl_window() && y >= 0) { + bitmap = GL_rect_to_nsbitmap(win, x, y, w, h); + } else { + while (win->window()) { + x += win->x(); + y += win->y(); + win = win->window(); + } + NSView *winview = nil; + if ( through_drawRect && Fl_Window::current() == win ) { + CGFloat epsilon = 0; + if (fl_mac_os_version >= 100600) epsilon = 0.5; // STR #2887 + rect = NSMakeRect(x - epsilon, y - epsilon, w, h); } - else { - rect = NSMakeRect(x, win->h()-(y+h), w, h); - // lock focus to win's view - winview = [fl_xid(win) contentView]; - [winview lockFocus]; + else { + rect = NSMakeRect(x, win->h()-(y+h), w, h); + // lock focus to win's view + winview = [fl_xid(win) contentView]; + [winview lockFocus]; } - NSBitmapImageRep *bitmap = [[[NSBitmapImageRep alloc] initWithFocusedViewRect:rect] autorelease]; - if ( !through_drawRect ) [winview unlockFocus]; + // The image depth is 3 until 10.5 and 4 with 10.6 and above + bitmap = [[NSBitmapImageRep alloc] initWithFocusedViewRect:rect]; + if ( !( through_drawRect && Fl_Window::current() == win) ) [winview unlockFocus]; + if (!bitmap) return nil; + } + + // it's necessary to capture also GL subwindows + for (Fl_X *flx = Fl_X::i(win)->xidChildren; flx; flx = flx->xidNext) { + Fl_Window *sub = flx->w; + if (!sub->as_gl_window() ) continue; + CGRect rsub = CGRectMake(sub->x(), win->h() -(sub->y()+sub->h()), sub->w(), sub->h()); + CGRect clip = CGRectMake(x, win->h()-(y+h), w, h); + clip = CGRectIntersection(rsub, clip); + if (CGRectIsNull(clip)) continue; + NSBitmapImageRep *childbitmap = rect_to_NSBitmapImageRep(sub, clip.origin.x - sub->x(), + win->h() - clip.origin.y - sub->y() - clip.size.height, clip.size.width, clip.size.height); + if (childbitmap) write_bitmap_inside(bitmap, w, childbitmap, + clip.origin.x - x, win->h() - clip.origin.y - clip.size.height - y ); + [childbitmap release]; + } return bitmap; } @@ -3904,35 +4051,39 @@ unsigned char *Fl_X::bitmap_from_window_rect(Fl_Window *win, int x, int y, int w } } } + [bitmap release]; return data; } -static void imgProviderReleaseData (void *info, const void *data, size_t size) +static void nsbitmapProviderReleaseData (void *info, const void *data, size_t size) { - delete[] (unsigned char *)data; + [(NSBitmapImageRep*)info release]; } CGImageRef Fl_X::CGImage_from_window_rect(Fl_Window *win, int x, int y, int w, int h) -// CFRelease the returned CGImageRef after use +/* Returns a capture of a rectangle of a mapped window as a CGImage. + With retina displays, the returned image has twice the width and height. + CFRelease the returned CGImageRef after use + */ { CGImageRef img; + NSBitmapImageRep *bitmap = rect_to_NSBitmapImageRep(win, x, y, w, h); if (fl_mac_os_version >= 100500) { - NSBitmapImageRep *bitmap = rect_to_NSBitmapImageRep(win, x, y, w, h); img = (CGImageRef)[bitmap performSelector:@selector(CGImage)]; // requires Mac OS 10.5 CGImageRetain(img); - } - else { - int bpp; - unsigned char *bitmap = bitmap_from_window_rect(win, x, y, w, h, &bpp); - if (!bitmap) return NULL; - CGColorSpaceRef lut = CGColorSpaceCreateDeviceRGB(); - CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, bitmap, w*h*bpp, imgProviderReleaseData); - img = CGImageCreate(w, h, 8, 8*bpp, w*bpp, lut, - bpp == 3 ? kCGImageAlphaNone : kCGImageAlphaPremultipliedLast, - provider, NULL, false, kCGRenderingIntentDefault); - CGColorSpaceRelease(lut); + [bitmap release]; + } else { + CGColorSpaceRef cspace = CGColorSpaceCreateDeviceRGB(); + CGDataProviderRef provider = CGDataProviderCreateWithData(bitmap, [bitmap bitmapData], + [bitmap bytesPerRow]*[bitmap pixelsHigh], + nsbitmapProviderReleaseData); + img = CGImageCreate([bitmap pixelsWide], [bitmap pixelsHigh], 8, [bitmap bitsPerPixel], [bitmap bytesPerRow], + cspace, + [bitmap bitsPerPixel] == 32 ? kCGImageAlphaPremultipliedLast : kCGImageAlphaNone, + provider, NULL, false, kCGRenderingIntentDefault); + CGColorSpaceRelease(cspace); CGDataProviderRelease(provider); - } + } return img; } diff --git a/src/Fl_win32.cxx b/src/Fl_win32.cxx index df86652d2..ea2f352ea 100644 --- a/src/Fl_win32.cxx +++ b/src/Fl_win32.cxx @@ -2683,11 +2683,14 @@ void Fl_Paged_Device::print_window(Fl_Window *win, int x_offset, int y_offset) fl_gc = GetDC(NULL); // get the screen device context // capture the 4 window sides from screen RECT r; GetWindowRect(fl_window, &r); + Window save_win = fl_window; + fl_window = NULL; // force use of read_win_rectangle() by fl_read_image() uchar *top_image = fl_read_image(NULL, r.left, r.top, ww, bt + by); uchar *left_image = fl_read_image(NULL, r.left, r.top, bx, wh); uchar *right_image = fl_read_image(NULL, r.right - bx, r.top, bx, wh); uchar *bottom_image = fl_read_image(NULL, r.left, r.bottom-by, ww, by); - ReleaseDC(NULL, fl_gc); fl_gc = save_gc; + fl_window = save_win; + ReleaseDC(NULL, fl_gc); fl_gc = save_gc; this->set_current(); // print the 4 window sides fl_draw_image(top_image, x_offset, y_offset, ww, bt + by, 3); diff --git a/src/fl_read_image.cxx b/src/fl_read_image.cxx index f253c2aa8..89ff3e49e 100644 --- a/src/fl_read_image.cxx +++ b/src/fl_read_image.cxx @@ -3,7 +3,7 @@ // // X11 image reading routines for the Fast Light Tool Kit (FLTK). // -// Copyright 1998-2010 by Bill Spitzak and others. +// Copyright 1998-2014 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 @@ -25,11 +25,151 @@ # include <stdio.h> #endif // DEBUG -#ifdef WIN32 -# include "fl_read_image_win32.cxx" -#elif defined(__APPLE__) +#if defined(__APPLE__) # include "fl_read_image_mac.cxx" #else +# include <FL/Fl_RGB_Image.H> +# include <FL/Fl_Window.H> +# include <FL/Fl_Plugin.H> +# include <FL/Fl_Device.H> + +static uchar *read_win_rectangle(uchar *p, int X, int Y, int w, int h, int alpha); + + +static void write_image_inside(Fl_RGB_Image *to, Fl_RGB_Image *from, int to_x, int to_y) +/* Copy the image "from" inside image "to" with its top-left angle at coordinates to_x, to_y. + Also, exchange top and bottom of "from". Image depth can differ between "to" and "from". + */ +{ + int to_ld = (to->ld() == 0? to->w() * to->d() : to->ld()); + int from_ld = (from->ld() == 0? from->w() * from->d() : from->ld()); + uchar *tobytes = (uchar*)to->array + to_y * to_ld + to_x * to->d(); + const uchar *frombytes = from->array + (from->h() - 1) * from_ld; + for (int i = from->h() - 1; i >= 0; i--) { + if (from->d() == to->d()) memcpy(tobytes, frombytes, from->w() * from->d()); + else { + for (int j = 0; j < from->w(); j++) { + memcpy(tobytes + j * to->d(), frombytes + j * from->d(), from->d()); + } + } + tobytes += to_ld; + frombytes -= from_ld; + } +} + +/* Captures rectangle x,y,w,h from a mapped window or GL window. + All sub-GL-windows that intersect x,y,w,h, and their subwindows, are also captured. + + Arguments when this function is initially called: + g: a window or GL window + p: as in fl_read_image() + x,y,w,h: a rectangle in window g's coordinates + alpha: as in fl_read_image() + full_img: NULL + + Arguments when this function recursively calls itself: + g: an Fl_Group + p: as above + x,y,w,h: a rectangle in g's coordinates if g is a window, or in g's parent window coords if g is a group + alpha: as above + full_img: NULL, or a previously captured image that encompasses the x,y,w,h rectangle and that + will be partially overwritten with the new capture + + Return value: + An Fl_RGB_Image* of depth 4 if alpha>0 or 3 if alpha = 0 containing the captured pixels. + */ +static Fl_RGB_Image *traverse_to_gl_subwindows(Fl_Group *g, uchar *p, int x, int y, int w, int h, int alpha, + Fl_RGB_Image *full_img) +{ + if ( g->as_gl_window() ) { + Fl_Plugin_Manager pm("fltk:device"); + Fl_Device_Plugin *pi = (Fl_Device_Plugin*)pm.plugin("opengl.device.fltk.org"); + if (!pi) return full_img; + Fl_RGB_Image *img = pi->rectangle_capture(g, x, y, w, h); // bottom to top image + if (full_img) full_img = img; // top and bottom will be exchanged later + else { // exchange top and bottom to get a proper FLTK image + uchar *data = ( p ? p : new uchar[img->w() * img->h() * (alpha?4:3)] ); + full_img = new Fl_RGB_Image(data, img->w(), img->h(), alpha?4:3); + if (!p) full_img->alloc_array = 1; + if (alpha) memset(data, alpha, img->w() * img->h() * 4); + write_image_inside(full_img, img, 0, 0); + delete img; + } + } + else if ( g->as_window() && (!full_img || (g->window() && g->window()->as_gl_window())) ) { + // the starting window or one inside a GL window + if (full_img) g->as_window()->make_current(); + uchar *image_data; + int alloc_img = (full_img != NULL || p == NULL); // false means use p, don't alloc new memory for image +#ifdef __APPLE_CC__ + // on Darwin + X11, read_win_rectangle() sometimes returns NULL when there are subwindows + do image_data = read_win_rectangle( (alloc_img ? NULL : p), x, y, w, h, alpha); while (!image_data); +#else + image_data = read_win_rectangle( (alloc_img ? NULL : p), x, y, w, h, alpha); +#endif + full_img = new Fl_RGB_Image(image_data, w, h, alpha?4:3); + if (alloc_img) full_img->alloc_array = 1; + } + int n = g->children(); + for (int i = 0; i < n; i++) { + Fl_Widget *c = g->child(i); + if ( !c->visible() || !c->as_group()) continue; + if ( c->as_window() ) { + int origin_x = x; // compute intersection of x,y,w,h and the c window + if (x < c->x()) origin_x = c->x(); + int origin_y = y; + if (y < c->y()) origin_y = c->y(); + int width = c->w(); + if (origin_x + width > c->x() + c->w()) width = c->x() + c->w() - origin_x; + if (origin_x + width > x + w) width = x + w - origin_x; + int height = c->w(); + if (origin_y + height > c->y() + c->h()) height = c->y() + c->h() - origin_y; + if (origin_y + height > y + h) height = y + h - origin_y; + if (width > 0 && height > 0) { + Fl_RGB_Image *img = traverse_to_gl_subwindows(c->as_window(), p, origin_x - c->x(), + origin_y - c->y(), width, height, alpha, full_img); + if (img == full_img) continue; + int top; + if (c->as_gl_window()) { + top = origin_y - y; + } else { + top = full_img->h() - (origin_y - y + img->h()); + } + write_image_inside(full_img, img, origin_x - x, top); + delete img; + } + } + else traverse_to_gl_subwindows(c->as_group(), p, x, y, w, h, alpha, full_img); + } + return full_img; +} + +// +// 'fl_read_image()' - Read an image from the current window or off-screen buffer +// this is the version for X11 and WIN32. The mac version is in fl_read_image_mac.cxx + +uchar * // O - Pixel buffer or NULL if failed +fl_read_image(uchar *p, // I - Pixel buffer or NULL to allocate + int X, // I - Left position + int Y, // I - Top position + int w, // I - Width of area to read + // negative allows capture of window title bar and frame (X11 only) + int h, // I - Height of area to read + int alpha)// I - Alpha value for image (0 for none) +{ + if (w < 0 || fl_find(fl_window) == 0) { // read from off_screen buffer or title bar and frame + return read_win_rectangle(p, X, Y, w, h, alpha); // this function has an X11 and a WIN32 version + } + Fl_RGB_Image *img = traverse_to_gl_subwindows(Fl_Window::current(), p, X, Y, w, h, alpha, NULL); + uchar *image_data = (uchar*)img->array; + img->alloc_array = 0; + delete img; + return image_data; +} + +#ifdef WIN32 +# include "fl_read_image_win32.cxx" // gives the WIN32 version of read_win_rectangle() +#else # include <X11/Xutil.h> # ifdef __sgi # include <X11/extensions/readdisplay.h> @@ -76,18 +216,9 @@ extern "C" { } } -// -// 'fl_read_image()' - Read an image from the current window. -// -uchar * // O - Pixel buffer or NULL if failed -fl_read_image(uchar *p, // I - Pixel buffer or NULL to allocate - int X, // I - Left position - int Y, // I - Top position - int w, // I - Width of area to read - // negative allows capture of window title bar and frame - int h, // I - Height of area to read - int alpha) { // I - Alpha value for image (0 for none) +static uchar *read_win_rectangle(uchar *p, int X, int Y, int w, int h, int alpha) +{ XImage *image; // Captured image int i, maxindex; // Looping vars int x, y; // Current X & Y in image @@ -495,7 +626,9 @@ fl_read_image(uchar *p, // I - Pixel buffer or NULL to allocate return p; } -#endif +#endif // !WIN32 + +#endif // !__APPLE__ // // End of "$Id$". diff --git a/src/fl_read_image_win32.cxx b/src/fl_read_image_win32.cxx index a811248aa..28f83017a 100644 --- a/src/fl_read_image_win32.cxx +++ b/src/fl_read_image_win32.cxx @@ -3,7 +3,7 @@ // // WIN32 image reading routines for the Fast Light Tool Kit (FLTK). // -// Copyright 1998-2010 by Bill Spitzak and others. +// Copyright 1998-2014 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 @@ -16,12 +16,8 @@ // http://www.fltk.org/str.php // -// -// 'fl_read_image()' - Read an image from the current window. -// - -uchar * // O - Pixel buffer or NULL if failed -fl_read_image(uchar *p, // I - Pixel buffer or NULL to allocate +static uchar * // O - Pixel buffer or NULL if failed +read_win_rectangle(uchar *p, // I - Pixel buffer or NULL to allocate int X, // I - Left position int Y, // I - Top position int w, // I - Width of area to read |
