UI: Allow Clipboard Copy/Paste Images
Adds operators to copy and paste to and from the OS clipboard, but only implemented for Windows. Pull Request: https://projects.blender.org/blender/blender/pulls/105833
This commit is contained in:
committed by
Harley Acheson
parent
29c2722753
commit
39bcf6bdc9
@@ -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
|
||||
|
||||
@@ -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.
|
||||
***************************************************************************************/
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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<std::chrono::milliseconds>(
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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, "<clipboard>");
|
||||
|
||||
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, "<memory>", 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
|
||||
* \{ */
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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...")
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
* \{ */
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
Reference in New Issue
Block a user