From bd1446a6eb5b845179bb850c0f371a323141e8b0 Mon Sep 17 00:00:00 2001 From: Manolo Gouy Date: Thu, 6 Nov 2014 16:48:57 +0000 Subject: =?UTF-8?q?Fix=20for=20STR#3142=20where=20fl=5Fread=5Fimage()=20co?= =?UTF-8?q?rrectly=20reads=20GL=20data=20under=20X11,=20but=20ignores=20th?= =?UTF-8?q?em=20under=20MSWindows=20and=20Mac=20OS=20X.=20Moreover,=20fl?= =?UTF-8?q?=5Fread=5Fimage()=20behaves=20differently=20with=20and=20withou?= =?UTF-8?q?t=20OS=20virtualization=20for=20X11=20and=20MSWindows.=20The=20?= =?UTF-8?q?patched=20function=20reads=20whatever=20is=20in=20the=20rectang?= =?UTF-8?q?le=20transmitted=20in=20arguments,=20with=20and=20without=20GL?= =?UTF-8?q?=20data,=20with=20and=20without=20subwindows,=20on=20=E2=80=98t?= =?UTF-8?q?rue=E2=80=99=20OS=20or=20on=20virtualized=20OS.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git-svn-id: file:///fltk/svn/fltk/branches/branch-1.3@10436 ea41ed52-d2ee-0310-a9c1-e6b18d33e121 --- src/Fl_Gl_Device_Plugin.cxx | 97 ++++++++++---------- src/Fl_Paged_Device.cxx | 9 +- src/Fl_cocoa.mm | 213 +++++++++++++++++++++++++++++++++++++------- src/Fl_win32.cxx | 5 +- src/fl_read_image.cxx | 165 ++++++++++++++++++++++++++++++---- src/fl_read_image_win32.cxx | 10 +-- 6 files changed, 387 insertions(+), 112 deletions(-) (limited to 'src') 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 #include #include "Fl_Gl_Choice.H" +#include #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 #endif // DEBUG -#ifdef WIN32 -# include "fl_read_image_win32.cxx" -#elif defined(__APPLE__) +#if defined(__APPLE__) # include "fl_read_image_mac.cxx" #else +# include +# include +# include +# include + +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 # ifdef __sgi # include @@ -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 -- cgit v1.2.3