diff --git a/intern/ghost/GHOST_C-api.h b/intern/ghost/GHOST_C-api.h index d0fe64aedfa..b341e9b5e39 100644 --- a/intern/ghost/GHOST_C-api.h +++ b/intern/ghost/GHOST_C-api.h @@ -911,6 +911,27 @@ extern char *GHOST_getClipboard(bool selection); */ extern void GHOST_putClipboard(const char *buffer, bool selection); +/** + * Returns GHOST_kSuccess if the clipboard contains an image. + */ +extern GHOST_TSuccess GHOST_hasClipboardImage(void); + +/** + * Get image data from the Clipboard + * \param r_width: the returned image width in pixels. + * \param r_height: the returned image height in pixels. + * \return pointer uint array in RGBA byte order. Caller must free. + */ +extern uint *GHOST_getClipboardImage(int *r_width, int *r_height); + +/** + * Put image data to the Clipboard + * \param rgba: uint array in RGBA byte order. + * \param width: the image width in pixels. + * \param height: the image height in pixels. + */ +extern GHOST_TSuccess GHOST_putClipboardImage(uint *rgba, int width, int height); + /** * Set the Console State * \param action: console state diff --git a/intern/ghost/GHOST_ISystem.hh b/intern/ghost/GHOST_ISystem.hh index de36dd19e93..7ace85f0265 100644 --- a/intern/ghost/GHOST_ISystem.hh +++ b/intern/ghost/GHOST_ISystem.hh @@ -471,6 +471,27 @@ class GHOST_ISystem { */ virtual void putClipboard(const char *buffer, bool selection) const = 0; + /** + * Returns GHOST_kSuccess if the clipboard contains an image. + */ + virtual GHOST_TSuccess hasClipboardImage(void) const = 0; + + /** + * Get image data from the Clipboard + * \param r_width: the returned image width in pixels. + * \param r_height: the returned image height in pixels. + * \return pointer uint array in RGBA byte order. Caller must free. + */ + virtual uint *getClipboardImage(int *r_width, int *r_height) const = 0; + + /** + * Put image data to the Clipboard + * \param rgba: uint array in RGBA byte order. + * \param width: the image width in pixels. + * \param height: the image height in pixels. + */ + virtual GHOST_TSuccess putClipboardImage(uint *rgba, int width, int height) const = 0; + /*************************************************************************************** * System Message Box. ***************************************************************************************/ diff --git a/intern/ghost/intern/GHOST_C-api.cc b/intern/ghost/intern/GHOST_C-api.cc index 9b9d9465811..45060fd5730 100644 --- a/intern/ghost/intern/GHOST_C-api.cc +++ b/intern/ghost/intern/GHOST_C-api.cc @@ -883,6 +883,24 @@ void GHOST_putClipboard(const char *buffer, bool selection) system->putClipboard(buffer, selection); } +GHOST_TSuccess GHOST_hasClipboardImage(void) +{ + GHOST_ISystem *system = GHOST_ISystem::getSystem(); + return system->hasClipboardImage(); +} + +uint *GHOST_getClipboardImage(int *r_width, int *r_height) +{ + GHOST_ISystem *system = GHOST_ISystem::getSystem(); + return system->getClipboardImage(r_width, r_height); +} + +GHOST_TSuccess GHOST_putClipboardImage(uint *rgba, int width, int height) +{ + GHOST_ISystem *system = GHOST_ISystem::getSystem(); + return system->putClipboardImage(rgba, width, height); +} + bool GHOST_setConsoleWindowState(GHOST_TConsoleWindowState action) { GHOST_ISystem *system = GHOST_ISystem::getSystem(); diff --git a/intern/ghost/intern/GHOST_System.cc b/intern/ghost/intern/GHOST_System.cc index d146e7cee78..92f4a29c538 100644 --- a/intern/ghost/intern/GHOST_System.cc +++ b/intern/ghost/intern/GHOST_System.cc @@ -42,6 +42,23 @@ GHOST_System::~GHOST_System() exit(); } +GHOST_TSuccess GHOST_System::hasClipboardImage(void) const +{ + return GHOST_kFailure; +} + +uint *GHOST_System::getClipboardImage(int * /*r_width*/, int * /*r_height*/) const +{ + return nullptr; +} + +GHOST_TSuccess GHOST_System::putClipboardImage(uint * /*rgba*/, + int /*width*/, + int /*height*/) const +{ + return GHOST_kFailure; +} + uint64_t GHOST_System::getMilliSeconds() const { return std::chrono::duration_cast( diff --git a/intern/ghost/intern/GHOST_System.hh b/intern/ghost/intern/GHOST_System.hh index 9ede2fda862..292ede327d5 100644 --- a/intern/ghost/intern/GHOST_System.hh +++ b/intern/ghost/intern/GHOST_System.hh @@ -330,6 +330,27 @@ class GHOST_System : public GHOST_ISystem { */ virtual void putClipboard(const char *buffer, bool selection) const = 0; + /** + * Returns GHOST_kSuccess if the clipboard contains an image. + */ + GHOST_TSuccess hasClipboardImage(void) const; + + /** + * Get image data from the Clipboard + * \param r_width: the returned image width in pixels. + * \param r_height: the returned image height in pixels. + * \return pointer uint array in RGBA byte order. Caller must free. + */ + uint *getClipboardImage(int *r_width, int *r_height) const; + + /** + * Put image data to the Clipboard + * \param rgba: uint array in RGBA byte order. + * \param width: the image width in pixels. + * \param height: the image height in pixels. + */ + GHOST_TSuccess putClipboardImage(uint *rgba, int width, int height) const; + /** * Show a system message box * \param title: The title of the message box. diff --git a/intern/ghost/intern/GHOST_SystemWin32.cc b/intern/ghost/intern/GHOST_SystemWin32.cc index 51a8a5d464e..7fd33304488 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.cc +++ b/intern/ghost/intern/GHOST_SystemWin32.cc @@ -26,6 +26,9 @@ #include "utf_winfunc.h" #include "utfconv.h" +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" + #include "GHOST_DisplayManagerWin32.hh" #include "GHOST_EventButton.hh" #include "GHOST_EventCursor.hh" @@ -2305,6 +2308,257 @@ void GHOST_SystemWin32::putClipboard(const char *buffer, bool selection) const } } +GHOST_TSuccess GHOST_SystemWin32::hasClipboardImage(void) const +{ + if (IsClipboardFormatAvailable(CF_DIBV5) || + IsClipboardFormatAvailable(RegisterClipboardFormat("PNG"))) { + return GHOST_kSuccess; + } + + return GHOST_kFailure; +} + +static uint *getClipboardImageDibV5(int *r_width, int *r_height) +{ + HANDLE hGlobal = GetClipboardData(CF_DIBV5); + if (hGlobal == nullptr) { + return nullptr; + } + + BITMAPV5HEADER *bitmapV5Header = (BITMAPV5HEADER *)GlobalLock(hGlobal); + if (bitmapV5Header == nullptr) { + return nullptr; + } + + int offset = bitmapV5Header->bV5Size + bitmapV5Header->bV5ClrUsed * sizeof(RGBQUAD); + + if (bitmapV5Header->bV5Compression == BI_BITFIELDS) { + offset += 12; + } + BYTE *buffer = (BYTE *)bitmapV5Header + offset; + int bitcount = bitmapV5Header->bV5BitCount; + int width = bitmapV5Header->bV5Width; + int height = bitmapV5Header->bV5Height; + *r_width = width; + *r_height = height; + + DWORD ColorMasks[4]; + ColorMasks[0] = bitmapV5Header->bV5RedMask ? bitmapV5Header->bV5RedMask : 0xff; + ColorMasks[1] = bitmapV5Header->bV5GreenMask ? bitmapV5Header->bV5GreenMask : 0xff00; + ColorMasks[2] = bitmapV5Header->bV5BlueMask ? bitmapV5Header->bV5BlueMask : 0xff0000; + ColorMasks[3] = bitmapV5Header->bV5AlphaMask ? bitmapV5Header->bV5AlphaMask : 0xff000000; + + /* Bit shifts needed for the ColorMasks. */ + DWORD ColorShifts[4]; + for (int i = 0; i < 4; i++) { + _BitScanForward(&ColorShifts[i], ColorMasks[i]); + } + + uchar *source = (uchar *)buffer; + uint *rgba = (uint *)malloc(width * height * 4); + uint8_t *target = (uint8_t *)rgba; + + if (bitmapV5Header->bV5Compression == BI_BITFIELDS && bitcount == 32) { + for (int h = 0; h < height; h++) { + for (int w = 0; w < width; w++, target += 4, source += 4) { + DWORD *pix = (DWORD *)source; + target[0] = uint8_t((*pix & ColorMasks[0]) >> ColorShifts[0]); + target[1] = uint8_t((*pix & ColorMasks[1]) >> ColorShifts[1]); + target[2] = uint8_t((*pix & ColorMasks[2]) >> ColorShifts[2]); + target[3] = uint8_t((*pix & ColorMasks[3]) >> ColorShifts[3]); + } + } + } + else if (bitmapV5Header->bV5Compression == BI_RGB && bitcount == 32) { + for (int h = 0; h < height; h++) { + for (int w = 0; w < width; w++, target += 4, source += 4) { + RGBQUAD *quad = (RGBQUAD *)source; + target[0] = uint8_t(quad->rgbRed); + target[1] = uint8_t(quad->rgbGreen); + target[2] = uint8_t(quad->rgbBlue); + target[3] = (bitmapV5Header->bV5AlphaMask) ? uint8_t(quad->rgbReserved) : 255; + } + } + } + else if (bitmapV5Header->bV5Compression == BI_RGB && bitcount == 24) { + int bytes_per_row = ((((width * bitcount) + 31) & ~31) >> 3); + int slack = bytes_per_row - (width * 3); + for (int h = 0; h < height; h++, source += slack) { + for (int w = 0; w < width; w++, target += 4, source += 3) { + RGBTRIPLE *triple = (RGBTRIPLE *)source; + target[0] = uint8_t(triple->rgbtRed); + target[1] = uint8_t(triple->rgbtGreen); + target[2] = uint8_t(triple->rgbtBlue); + target[3] = 255; + } + } + } + + GlobalUnlock(hGlobal); + return rgba; +} + +/* Works with any image format that ImBuf can load. */ +static uint *getClipboardImageImBuf(int *r_width, int *r_height, UINT format) +{ + HANDLE hGlobal = GetClipboardData(format); + if (hGlobal == nullptr) { + return nullptr; + } + + LPVOID pMem = GlobalLock(hGlobal); + if (!pMem) { + return nullptr; + } + + uint *rgba = nullptr; + + ImBuf *ibuf = IMB_ibImageFromMemory( + (uchar *)pMem, GlobalSize(hGlobal), IB_rect, nullptr, ""); + + if (ibuf) { + *r_width = ibuf->x; + *r_height = ibuf->y; + rgba = (uint *)malloc(4 * ibuf->x * ibuf->y); + memcpy(rgba, ibuf->rect, 4 * ibuf->x * ibuf->y); + IMB_freeImBuf(ibuf); + } + + GlobalUnlock(hGlobal); + return rgba; +} + +uint *GHOST_SystemWin32::getClipboardImage(int *r_width, int *r_height) const +{ + if (!OpenClipboard(nullptr)) { + return nullptr; + } + + /* Synthesized formats are placed after posted formats. */ + UINT cfPNG = RegisterClipboardFormat("PNG"); + UINT format = 0; + for (int cf = EnumClipboardFormats(0); cf; cf = EnumClipboardFormats(cf)) { + if (ELEM(cf, CF_DIBV5, cfPNG)) { + format = cf; + } + if (cf == CF_DIBV5 || (cf == CF_BITMAP && format == cfPNG)) { + break; /* Favor CF_DIBV5, but not if synthesized. */ + } + } + + uint *rgba = nullptr; + + if (format == CF_DIBV5) { + rgba = getClipboardImageDibV5(r_width, r_height); + } + else if (format == cfPNG) { + rgba = getClipboardImageImBuf(r_width, r_height, cfPNG); + } + else { + *r_width = 0; + *r_height = 0; + } + + CloseClipboard(); + return rgba; +} + +static bool putClipboardImageDibV5(uint *rgba, int width, int height) +{ + DWORD size_pixels = width * height * 4; + + /* Pixel data is 12 bytes after the header. */ + HGLOBAL hMem = GlobalAlloc(GHND, sizeof(BITMAPV5HEADER) + 12 + size_pixels); + if (!hMem) { + return false; + } + + BITMAPV5HEADER *hdr = (BITMAPV5HEADER *)GlobalLock(hMem); + if (!hdr) { + GlobalFree(hMem); + return false; + } + + hdr->bV5Size = sizeof(BITMAPV5HEADER); + hdr->bV5Width = width; + hdr->bV5Height = height; + hdr->bV5Planes = 1; + hdr->bV5BitCount = 32; + hdr->bV5SizeImage = size_pixels; + hdr->bV5Compression = BI_BITFIELDS; + hdr->bV5RedMask = 0x000000ff; + hdr->bV5GreenMask = 0x0000ff00; + hdr->bV5BlueMask = 0x00ff0000; + hdr->bV5AlphaMask = 0xff000000; + hdr->bV5CSType = LCS_sRGB; + hdr->bV5Intent = LCS_GM_IMAGES; + hdr->bV5ClrUsed = 0; + + memcpy((char *)hdr + sizeof(BITMAPV5HEADER) + 12, rgba, size_pixels); + + GlobalUnlock(hMem); + + if (!SetClipboardData(CF_DIBV5, hMem)) { + GlobalFree(hMem); + return false; + } + + return true; +} + +static bool putClipboardImagePNG(uint *rgba, int width, int height) +{ + UINT cf = RegisterClipboardFormat("PNG"); + + /* Load buffer into ImBuf, convert to PNG. */ + ImBuf *ibuf = IMB_allocFromBuffer(rgba, nullptr, width, height, 32); + ibuf->ftype = IMB_FTYPE_PNG; + ibuf->foptions.quality = 15; + if (!IMB_saveiff(ibuf, "", IB_rect | IB_mem)) { + IMB_freeImBuf(ibuf); + return false; + } + + HGLOBAL hMem = GlobalAlloc(GHND, ibuf->encodedbuffersize); + if (!hMem) { + IMB_freeImBuf(ibuf); + return false; + } + + LPVOID pMem = GlobalLock(hMem); + if (!pMem) { + IMB_freeImBuf(ibuf); + GlobalFree(hMem); + return false; + } + + memcpy(pMem, ibuf->encodedbuffer, ibuf->encodedbuffersize); + + GlobalUnlock(hMem); + IMB_freeImBuf(ibuf); + + if (!SetClipboardData(cf, hMem)) { + GlobalFree(hMem); + return false; + } + + return true; +} + +GHOST_TSuccess GHOST_SystemWin32::putClipboardImage(uint *rgba, int width, int height) const +{ + if (!OpenClipboard(nullptr) || !EmptyClipboard()) { + return GHOST_kFailure; + } + + bool ok = putClipboardImageDibV5(rgba, width, height) && + putClipboardImagePNG(rgba, width, height); + + CloseClipboard(); + + return (ok) ? GHOST_kSuccess : GHOST_kFailure; +} + /* -------------------------------------------------------------------- */ /** \name Message Box * \{ */ diff --git a/intern/ghost/intern/GHOST_SystemWin32.hh b/intern/ghost/intern/GHOST_SystemWin32.hh index f434be24448..2f91ddf345b 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.hh +++ b/intern/ghost/intern/GHOST_SystemWin32.hh @@ -215,6 +215,27 @@ class GHOST_SystemWin32 : public GHOST_System { */ void putClipboard(const char *buffer, bool selection) const; + /** + * Returns GHOST_kSuccess if the clipboard contains an image. + */ + GHOST_TSuccess hasClipboardImage(void) const; + + /** + * Get image data from the Clipboard + * \param r_width: the returned image width in pixels. + * \param r_height: the returned image height in pixels. + * \return pointer uint array in RGBA byte order. Caller must free. + */ + uint *getClipboardImage(int *r_width, int *r_height) const; + + /** + * Put image data to the Clipboard + * \param rgba: uint array in RGBA byte order. + * \param width: the image width in pixels. + * \param height: the image height in pixels. + */ + GHOST_TSuccess putClipboardImage(uint *rgba, int width, int height) const; + /** * Show a system message box * \param title: The title of the message box. diff --git a/scripts/startup/bl_ui/space_image.py b/scripts/startup/bl_ui/space_image.py index d0f17c582d5..6b6cf70be21 100644 --- a/scripts/startup/bl_ui/space_image.py +++ b/scripts/startup/bl_ui/space_image.py @@ -184,6 +184,8 @@ class IMAGE_MT_image(Menu): bl_label = "Image" def draw(self, context): + import sys + layout = self.layout sima = context.space_data @@ -207,6 +209,11 @@ class IMAGE_MT_image(Menu): layout.separator() + if sys.platform[:3] == "win": + layout.operator("image.clipboard_copy", text="Copy") + layout.operator("image.clipboard_paste", text="Paste") + layout.separator() + if ima: layout.operator("image.save", text="Save", icon='FILE_TICK') layout.operator("image.save_as", text="Save As...") diff --git a/source/blender/editors/space_image/image_intern.h b/source/blender/editors/space_image/image_intern.h index 505c097ea9b..9b31513b2c3 100644 --- a/source/blender/editors/space_image/image_intern.h +++ b/source/blender/editors/space_image/image_intern.h @@ -62,6 +62,8 @@ void IMAGE_OT_save_sequence(struct wmOperatorType *ot); void IMAGE_OT_save_all_modified(struct wmOperatorType *ot); void IMAGE_OT_pack(struct wmOperatorType *ot); void IMAGE_OT_unpack(struct wmOperatorType *ot); +void IMAGE_OT_clipboard_copy(struct wmOperatorType *ot); +void IMAGE_OT_clipboard_paste(struct wmOperatorType *ot); void IMAGE_OT_flip(struct wmOperatorType *ot); void IMAGE_OT_invert(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index 1b3f302fac0..aae687df4e7 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -71,6 +71,7 @@ #include "ED_screen.h" #include "ED_space_api.h" #include "ED_util.h" +#include "ED_undo.h" #include "ED_util_imbuf.h" #include "ED_uvedit.h" @@ -2834,6 +2835,125 @@ void IMAGE_OT_flip(wmOperatorType *ot) /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Clipboard Copy Operator + * \{ */ + +static int image_clipboard_copy_exec(bContext *C, wmOperator *op) +{ + Image *ima = image_from_context(C); + if (ima == NULL) { + return false; + } + + if (G.is_rendering && ima->source == IMA_SRC_VIEWER) { + BKE_report(op->reports, RPT_ERROR, "Images cannot be copied while rendering"); + return false; + } + + ImageUser *iuser = image_user_from_context(C); + WM_cursor_set(CTX_wm_window(C), WM_CURSOR_WAIT); + + void *lock; + ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, &lock); + if (ibuf == NULL) { + BKE_image_release_ibuf(ima, ibuf, lock); + WM_cursor_set(CTX_wm_window(C), WM_CURSOR_DEFAULT); + return OPERATOR_CANCELLED; + } + + WM_clipboard_image_set(ibuf); + BKE_image_release_ibuf(ima, ibuf, lock); + WM_cursor_set(CTX_wm_window(C), WM_CURSOR_DEFAULT); + + return OPERATOR_FINISHED; +} + +static bool image_clipboard_copy_poll(bContext *C) +{ + if (!image_from_context_has_data_poll(C)) { + CTX_wm_operator_poll_msg_set(C, "No images available"); + return false; + } + + return true; +} + +void IMAGE_OT_clipboard_copy(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Copy Image"; + ot->idname = "IMAGE_OT_clipboard_copy"; + ot->description = "Copy the image to the clipboard"; + + /* api callbacks */ + ot->exec = image_clipboard_copy_exec; + ot->poll = image_clipboard_copy_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Clipboard Paste Operator + * \{ */ + +static int image_clipboard_paste_exec(bContext *C, wmOperator *op) +{ + + WM_cursor_set(CTX_wm_window(C), WM_CURSOR_WAIT); + + ImBuf *ibuf = WM_clipboard_image_get(); + if (!ibuf) { + WM_cursor_set(CTX_wm_window(C), WM_CURSOR_DEFAULT); + return OPERATOR_CANCELLED; + } + + ED_undo_push_op(C, op); + + Main *bmain = CTX_data_main(C); + SpaceImage *sima = CTX_wm_space_image(C); + Image *ima = BKE_image_add_from_imbuf(bmain, ibuf, "Clipboard"); + IMB_freeImBuf(ibuf); + + ED_space_image_set(bmain, sima, ima, false); + BKE_image_signal(bmain, ima, (sima) ? &sima->iuser : NULL, IMA_SIGNAL_USER_NEW_IMAGE); + WM_event_add_notifier(C, NC_IMAGE | NA_ADDED, ima); + + WM_cursor_set(CTX_wm_window(C), WM_CURSOR_DEFAULT); + + return OPERATOR_FINISHED; +} + +static bool image_clipboard_paste_poll(bContext *C) +{ + if (!WM_clipboard_image_available()) { + CTX_wm_operator_poll_msg_set(C, "No compatible images are on the clipboard"); + return false; + } + + return true; +} + +void IMAGE_OT_clipboard_paste(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Paste Image"; + ot->idname = "IMAGE_OT_clipboard_paste"; + ot->description = "Paste new image from the clipboard"; + + /* api callbacks */ + ot->exec = image_clipboard_paste_exec; + ot->poll = image_clipboard_paste_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER; +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Invert Operators * \{ */ diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c index 46a51b8bca1..62f2de591ce 100644 --- a/source/blender/editors/space_image/space_image.c +++ b/source/blender/editors/space_image/space_image.c @@ -216,6 +216,8 @@ static void image_operatortypes(void) WM_operatortype_append(IMAGE_OT_save_all_modified); WM_operatortype_append(IMAGE_OT_pack); WM_operatortype_append(IMAGE_OT_unpack); + WM_operatortype_append(IMAGE_OT_clipboard_copy); + WM_operatortype_append(IMAGE_OT_clipboard_paste); WM_operatortype_append(IMAGE_OT_flip); WM_operatortype_append(IMAGE_OT_invert); diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index b72008f1023..cd74b283151 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -1572,6 +1572,27 @@ char *WM_clipboard_text_get(bool selection, int *r_len); char *WM_clipboard_text_get_firstline(bool selection, int *r_len); void WM_clipboard_text_set(const char *buf, bool selection); +/** + * Returns true if the clipboard contains an image. + */ +bool WM_clipboard_image_available(void); + +/** + * Get image data from the Clipboard + * \param r_width: the returned image width in pixels. + * \param r_height: the returned image height in pixels. + * \return pointer uint array in RGBA byte order. Caller must free. + */ +struct ImBuf *WM_clipboard_image_get(void); + +/** + * Put image data to the Clipboard + * \param rgba: uint array in RGBA byte order. + * \param width: the image width in pixels. + * \param height: the image height in pixels. + */ +bool WM_clipboard_image_set(struct ImBuf *ibuf); + /* progress */ void WM_progress_set(struct wmWindow *win, float progress); diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index 118af36371b..c2648c00349 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -60,6 +60,9 @@ #include "ED_scene.h" #include "ED_screen.h" +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" + #include "UI_interface.h" #include "UI_interface_icons.h" @@ -2062,6 +2065,45 @@ void WM_clipboard_text_set(const char *buf, bool selection) } } +bool WM_clipboard_image_available(void) +{ + return (bool)GHOST_hasClipboardImage(); +} + +ImBuf *WM_clipboard_image_get(void) +{ + uint width, height; + + uint *rgba = GHOST_getClipboardImage(&width, &height); + if (!rgba) { + return NULL; + } + + ImBuf *ibuf = IMB_allocFromBuffer(rgba, NULL, width, height, 4); + free(rgba); + + return ibuf; +} + +bool WM_clipboard_image_set(ImBuf *ibuf) +{ + bool free_byte_buffer = false; + if (ibuf->rect == NULL) { + /* Add a byte buffer if it does not have one. */ + IMB_rect_from_float(ibuf); + free_byte_buffer = true; + } + + bool success = (bool)GHOST_putClipboardImage(ibuf->rect, ibuf->x, ibuf->y); + + if (free_byte_buffer) { + /* Remove the byte buffer if we added it. */ + imb_freerectImBuf(ibuf); + } + + return success; +} + /** \} */ /* -------------------------------------------------------------------- */