mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-10 19:13:29 +00:00
Use clip library to copy & paste images/custom data
With this change we finally support copy & paste images into the OS X pasteboard. (Fix #925, fix #533, fix #172.)
This commit is contained in:
parent
60fd6072e9
commit
c99a187256
@ -421,6 +421,7 @@ add_library(app-lib
|
||||
ui_context.cpp
|
||||
util/autocrop.cpp
|
||||
util/clipboard.cpp
|
||||
util/clipboard_native.cpp
|
||||
util/create_cel_copy.cpp
|
||||
util/expand_cel_canvas.cpp
|
||||
util/filetoks.cpp
|
||||
|
@ -28,21 +28,12 @@
|
||||
#include "app/ui/timeline.h"
|
||||
#include "app/ui_context.h"
|
||||
#include "app/util/clipboard.h"
|
||||
#include "app/util/clipboard_native.h"
|
||||
#include "app/util/new_image_from_mask.h"
|
||||
#include "base/shared_ptr.h"
|
||||
#include "doc/doc.h"
|
||||
#include "render/quantization.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define USE_NATIVE_WIN32_CLIPBOARD
|
||||
#endif
|
||||
|
||||
#ifdef USE_NATIVE_WIN32_CLIPBOARD
|
||||
#include <windows.h>
|
||||
|
||||
#include "app/util/clipboard_win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
namespace app {
|
||||
@ -112,6 +103,8 @@ ClipboardManager::ClipboardManager()
|
||||
ASSERT(!g_instance);
|
||||
g_instance = this;
|
||||
|
||||
register_native_clipboard_formats();
|
||||
|
||||
clipboard_range.observeUIContext();
|
||||
}
|
||||
|
||||
@ -134,18 +127,31 @@ ClipboardManager* ClipboardManager::instance()
|
||||
return g_instance;
|
||||
}
|
||||
|
||||
static void set_clipboard_image(Image* image, Mask* mask, Palette* palette, bool set_system_clipboard)
|
||||
static void set_clipboard_image(Image* image,
|
||||
Mask* mask,
|
||||
Palette* palette,
|
||||
bool set_system_clipboard,
|
||||
bool image_source_is_transparent)
|
||||
{
|
||||
clipboard_palette.reset(palette);
|
||||
clipboard_picks.clear();
|
||||
clipboard_image.reset(image);
|
||||
clipboard_mask.reset(mask);
|
||||
|
||||
// copy to the Windows clipboard
|
||||
#ifdef USE_NATIVE_WIN32_CLIPBOARD
|
||||
if (set_system_clipboard)
|
||||
set_win32_clipboard_bitmap(image, mask, palette);
|
||||
#endif
|
||||
// Copy image to the native clipboard
|
||||
if (set_system_clipboard) {
|
||||
color_t oldMask;
|
||||
if (image) {
|
||||
oldMask = image->maskColor();
|
||||
if (!image_source_is_transparent)
|
||||
image->setMaskColor(-1);
|
||||
}
|
||||
|
||||
set_native_clipboard_bitmap(image, mask, palette);
|
||||
|
||||
if (image && !image_source_is_transparent)
|
||||
image->setMaskColor(oldMask);
|
||||
}
|
||||
|
||||
clipboard_range.invalidate();
|
||||
}
|
||||
@ -164,20 +170,22 @@ static bool copy_from_document(const Site& site)
|
||||
const Mask* mask = document->mask();
|
||||
const Palette* pal = document->sprite()->palette(site.frame());
|
||||
|
||||
set_clipboard_image(image,
|
||||
(mask ? new Mask(*mask): nullptr),
|
||||
(pal ? new Palette(*pal): nullptr), true);
|
||||
set_clipboard_image(
|
||||
image,
|
||||
(mask ? new Mask(*mask): nullptr),
|
||||
(pal ? new Palette(*pal): nullptr),
|
||||
true,
|
||||
site.layer() && !site.layer()->isBackground());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ClipboardFormat get_current_format()
|
||||
{
|
||||
#ifdef USE_NATIVE_WIN32_CLIPBOARD
|
||||
if (win32_clipboard_contains_bitmap())
|
||||
// Check if the native clipboard has an image
|
||||
if (has_native_clipboard_bitmap())
|
||||
return ClipboardImage;
|
||||
#endif
|
||||
|
||||
if (clipboard_image)
|
||||
else if (clipboard_image)
|
||||
return ClipboardImage;
|
||||
else if (clipboard_range.valid())
|
||||
return ClipboardDocumentRange;
|
||||
@ -200,7 +208,7 @@ void get_document_range_info(Document** document, DocumentRange* range)
|
||||
|
||||
void clear_content()
|
||||
{
|
||||
set_clipboard_image(nullptr, nullptr, nullptr, true);
|
||||
set_clipboard_image(nullptr, nullptr, nullptr, true, false);
|
||||
}
|
||||
|
||||
void cut(ContextWriter& writer)
|
||||
@ -255,7 +263,8 @@ void copy_image(const Image* image, const Mask* mask, const Palette* pal)
|
||||
set_clipboard_image(
|
||||
Image::createCopy(image),
|
||||
(mask ? new Mask(*mask): nullptr),
|
||||
(pal ? new Palette(*pal): nullptr), true);
|
||||
(pal ? new Palette(*pal): nullptr),
|
||||
true, false);
|
||||
}
|
||||
|
||||
void copy_palette(const Palette* palette, const doc::PalettePicks& picks)
|
||||
@ -265,7 +274,8 @@ void copy_palette(const Palette* palette, const doc::PalettePicks& picks)
|
||||
|
||||
set_clipboard_image(nullptr,
|
||||
nullptr,
|
||||
new Palette(*palette), true);
|
||||
new Palette(*palette),
|
||||
true, false);
|
||||
clipboard_picks = picks;
|
||||
}
|
||||
|
||||
@ -281,17 +291,16 @@ void paste()
|
||||
switch (get_current_format()) {
|
||||
|
||||
case clipboard::ClipboardImage: {
|
||||
#ifdef USE_NATIVE_WIN32_CLIPBOARD
|
||||
// Get the image from the clipboard.
|
||||
{
|
||||
Image* win32_image = NULL;
|
||||
Mask* win32_mask = NULL;
|
||||
Palette* win32_palette = NULL;
|
||||
get_win32_clipboard_bitmap(win32_image, win32_mask, win32_palette);
|
||||
if (win32_image)
|
||||
set_clipboard_image(win32_image, win32_mask, win32_palette, false);
|
||||
Image* native_image = nullptr;
|
||||
Mask* native_mask = nullptr;
|
||||
Palette* native_palette = nullptr;
|
||||
get_native_clipboard_bitmap(&native_image, &native_mask, &native_palette);
|
||||
if (native_image)
|
||||
set_clipboard_image(native_image, native_mask, native_palette,
|
||||
false, false);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!clipboard_image)
|
||||
return;
|
||||
@ -509,18 +518,18 @@ void paste()
|
||||
|
||||
bool get_image_size(gfx::Size& size)
|
||||
{
|
||||
#ifdef USE_NATIVE_WIN32_CLIPBOARD
|
||||
// Get the image from the clipboard.
|
||||
return get_win32_clipboard_bitmap_size(size);
|
||||
#if defined(_WIN32) || defined(__APPLE__)
|
||||
if (get_native_clipboard_bitmap_size(&size))
|
||||
return true;
|
||||
#else
|
||||
if (clipboard_image) {
|
||||
size.w = clipboard_image->width();
|
||||
size.h = clipboard_image->height();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Palette* get_palette()
|
||||
|
296
src/app/util/clipboard_native.cpp
Normal file
296
src/app/util/clipboard_native.cpp
Normal file
@ -0,0 +1,296 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2016 David Capello
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "app/util/clipboard_native.h"
|
||||
|
||||
#include "base/serialization.h"
|
||||
#include "base/unique_ptr.h"
|
||||
#include "clip/clip.h"
|
||||
#include "doc/color_scales.h"
|
||||
#include "doc/image.h"
|
||||
#include "doc/image_impl.h"
|
||||
#include "doc/image_io.h"
|
||||
#include "doc/mask_io.h"
|
||||
#include "doc/palette_io.h"
|
||||
#include "gfx/size.h"
|
||||
#include "she/display.h"
|
||||
#include "she/system.h"
|
||||
#include "ui/alert.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
namespace app {
|
||||
namespace clipboard {
|
||||
|
||||
using namespace base::serialization;
|
||||
using namespace base::serialization::little_endian;
|
||||
|
||||
namespace {
|
||||
clip::format custom_image_format = 0;
|
||||
|
||||
void* native_display_handle() {
|
||||
return she::instance()->defaultDisplay()->nativeHandle();
|
||||
}
|
||||
|
||||
void custom_error_handler(clip::ErrorCode code) {
|
||||
switch (code) {
|
||||
case clip::ErrorCode::CannotLock:
|
||||
ui::Alert::show("Error<<Cannot access to the clipboard.\nMaybe other application is using it.||&OK");
|
||||
break;
|
||||
case clip::ErrorCode::ImageNotSupported:
|
||||
ui::Alert::show("Error<<The current clipboard image format is not supported.||&OK");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void register_native_clipboard_formats()
|
||||
{
|
||||
clip::set_error_handler(custom_error_handler);
|
||||
custom_image_format = clip::register_format("org.aseprite.Image");
|
||||
}
|
||||
|
||||
bool has_native_clipboard_bitmap()
|
||||
{
|
||||
return clip::has(clip::image_format());
|
||||
}
|
||||
|
||||
bool set_native_clipboard_bitmap(const doc::Image* image,
|
||||
const doc::Mask* mask,
|
||||
const doc::Palette* palette)
|
||||
{
|
||||
clip::lock l(native_display_handle());
|
||||
if (!l.locked())
|
||||
return false;
|
||||
|
||||
l.clear();
|
||||
|
||||
if (!image)
|
||||
return false;
|
||||
|
||||
// Set custom clipboard formats
|
||||
if (custom_image_format) {
|
||||
std::stringstream os;
|
||||
write32(os,
|
||||
(image ? 1: 0) |
|
||||
(mask ? 2: 0) |
|
||||
(palette ? 4: 0));
|
||||
if (image) doc::write_image(os, image);
|
||||
if (mask) doc::write_mask(os, mask);
|
||||
if (palette) doc::write_palette(os, palette);
|
||||
|
||||
if (os.good()) {
|
||||
size_t size = (size_t)os.tellp();
|
||||
if (size > 0) {
|
||||
std::vector<char> data(size);
|
||||
os.seekp(0);
|
||||
os.read(&data[0], size);
|
||||
|
||||
l.set_data(custom_image_format, &data[0], size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clip::image_spec spec;
|
||||
spec.width = image->width();
|
||||
spec.height = image->height();
|
||||
spec.bits_per_pixel = 32;
|
||||
spec.bytes_per_row = (image->pixelFormat() == doc::IMAGE_RGB ?
|
||||
image->getRowStrideSize(): 4*spec.width);
|
||||
spec.red_mask = doc::rgba_r_mask;
|
||||
spec.green_mask = doc::rgba_g_mask;
|
||||
spec.blue_mask = doc::rgba_b_mask;
|
||||
spec.alpha_mask = doc::rgba_a_mask;
|
||||
spec.red_shift = doc::rgba_r_shift;
|
||||
spec.green_shift = doc::rgba_g_shift;
|
||||
spec.blue_shift = doc::rgba_b_shift;
|
||||
spec.alpha_shift = doc::rgba_a_shift;
|
||||
|
||||
switch (image->pixelFormat()) {
|
||||
case doc::IMAGE_RGB: {
|
||||
// We use the RGB image data directly
|
||||
clip::image img(image->getPixelAddress(0, 0), spec);
|
||||
l.set_image(img);
|
||||
break;
|
||||
}
|
||||
case doc::IMAGE_GRAYSCALE: {
|
||||
clip::image img(spec);
|
||||
const doc::LockImageBits<doc::GrayscaleTraits> bits(image);
|
||||
auto it = bits.begin();
|
||||
uint32_t* dst = (uint32_t*)img.data();
|
||||
for (int y=0; y<image->height(); ++y) {
|
||||
for (int x=0; x<image->width(); ++x, ++it) {
|
||||
doc::color_t c = *it;
|
||||
*(dst++) = doc::rgba(doc::graya_getv(c),
|
||||
doc::graya_getv(c),
|
||||
doc::graya_getv(c),
|
||||
doc::graya_geta(c));
|
||||
}
|
||||
}
|
||||
l.set_image(img);
|
||||
break;
|
||||
}
|
||||
case doc::IMAGE_INDEXED: {
|
||||
clip::image img(spec);
|
||||
const doc::LockImageBits<doc::IndexedTraits> bits(image);
|
||||
auto it = bits.begin();
|
||||
uint32_t* dst = (uint32_t*)img.data();
|
||||
for (int y=0; y<image->height(); ++y) {
|
||||
for (int x=0; x<image->width(); ++x, ++it) {
|
||||
doc::color_t c = palette->getEntry(*it);
|
||||
|
||||
// Use alpha=0 for mask color
|
||||
if (*it == image->maskColor())
|
||||
c &= doc::rgba_rgb_mask;
|
||||
|
||||
*(dst++) = c;
|
||||
}
|
||||
}
|
||||
l.set_image(img);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool get_native_clipboard_bitmap(doc::Image** image,
|
||||
doc::Mask** mask,
|
||||
doc::Palette** palette)
|
||||
{
|
||||
*image = nullptr;
|
||||
*mask = nullptr;
|
||||
*palette = nullptr;
|
||||
|
||||
clip::lock l(native_display_handle());
|
||||
if (!l.locked())
|
||||
return false;
|
||||
|
||||
// Prefer the custom format (to avoid losing mask and palette)
|
||||
if (l.is_convertible(custom_image_format)) {
|
||||
size_t size = l.get_data_length(custom_image_format);
|
||||
if (size > 0) {
|
||||
std::vector<char> buf(size);
|
||||
if (l.get_data(custom_image_format, &buf[0], size)) {
|
||||
std::stringstream is;
|
||||
is.write(&buf[0], size);
|
||||
is.seekp(0);
|
||||
|
||||
int bits = read32(is);
|
||||
if (bits & 1) *image = doc::read_image(is, false);
|
||||
if (bits & 2) *mask = doc::read_mask(is);
|
||||
if (bits & 4) *palette = doc::read_palette(is);
|
||||
if (image)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!l.is_convertible(clip::image_format()))
|
||||
return false;
|
||||
|
||||
clip::image img;
|
||||
if (!l.get_image(img))
|
||||
return false;
|
||||
|
||||
const clip::image_spec& spec = img.spec();
|
||||
|
||||
base::UniquePtr<doc::Image> dst(
|
||||
doc::Image::create(doc::IMAGE_RGB,
|
||||
spec.width, spec.height));
|
||||
|
||||
switch (spec.bits_per_pixel) {
|
||||
case 64: {
|
||||
doc::LockImageBits<doc::RgbTraits> bits(dst.get(), doc::Image::WriteLock);
|
||||
auto it = bits.begin();
|
||||
for (unsigned long y=0; y<spec.height; ++y) {
|
||||
const uint64_t* src = (const uint64_t*)(img.data()+spec.bytes_per_row*y);
|
||||
for (unsigned long x=0; x<spec.width; ++x, ++it, ++src) {
|
||||
uint64_t c = *((const uint64_t*)src);
|
||||
*it = doc::rgba(
|
||||
uint8_t((c & spec.red_mask) >> spec.red_shift >> 8),
|
||||
uint8_t((c & spec.green_mask) >> spec.green_shift >> 8),
|
||||
uint8_t((c & spec.blue_mask) >> spec.blue_shift >> 8),
|
||||
uint8_t((c & spec.alpha_mask) >> spec.alpha_shift >> 8));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 32: {
|
||||
doc::LockImageBits<doc::RgbTraits> bits(dst.get(), doc::Image::WriteLock);
|
||||
auto it = bits.begin();
|
||||
for (unsigned long y=0; y<spec.height; ++y) {
|
||||
const uint32_t* src = (const uint32_t*)(img.data()+spec.bytes_per_row*y);
|
||||
for (unsigned long x=0; x<spec.width; ++x, ++it, ++src) {
|
||||
const uint32_t c = *((const uint32_t*)src);
|
||||
*it = doc::rgba(
|
||||
uint8_t((c & spec.red_mask ) >> spec.red_shift ),
|
||||
uint8_t((c & spec.green_mask) >> spec.green_shift),
|
||||
uint8_t((c & spec.blue_mask ) >> spec.blue_shift ),
|
||||
uint8_t((c & spec.alpha_mask) >> spec.alpha_shift));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 24: {
|
||||
doc::LockImageBits<doc::RgbTraits> bits(dst.get(), doc::Image::WriteLock);
|
||||
auto it = bits.begin();
|
||||
for (unsigned long y=0; y<spec.height; ++y) {
|
||||
const char* src = (const char*)(img.data()+spec.bytes_per_row*y);
|
||||
for (unsigned long x=0; x<spec.width; ++x, ++it, src+=3) {
|
||||
unsigned long c = *((const unsigned long*)src);
|
||||
*it = doc::rgba(
|
||||
uint8_t((c & spec.red_mask) >> spec.red_shift),
|
||||
uint8_t((c & spec.green_mask) >> spec.green_shift),
|
||||
uint8_t((c & spec.blue_mask) >> spec.blue_shift),
|
||||
255);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 16: {
|
||||
doc::LockImageBits<doc::RgbTraits> bits(dst.get(), doc::Image::WriteLock);
|
||||
auto it = bits.begin();
|
||||
for (unsigned long y=0; y<spec.height; ++y) {
|
||||
const uint16_t* src = (const uint16_t*)(img.data()+spec.bytes_per_row*y);
|
||||
for (unsigned long x=0; x<spec.width; ++x, ++it, ++src) {
|
||||
const uint16_t c = *((const uint16_t*)src);
|
||||
*it = doc::rgba(
|
||||
doc::scale_5bits_to_8bits((c & spec.red_mask ) >> spec.red_shift ),
|
||||
doc::scale_6bits_to_8bits((c & spec.green_mask) >> spec.green_shift),
|
||||
doc::scale_5bits_to_8bits((c & spec.blue_mask ) >> spec.blue_shift ),
|
||||
255);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*image = dst.release();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool get_native_clipboard_bitmap_size(gfx::Size* size)
|
||||
{
|
||||
clip::image_spec spec;
|
||||
if (clip::get_image_spec(spec)) {
|
||||
size->w = spec.width;
|
||||
size->h = spec.height;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace clipboard
|
||||
} // namespace app
|
36
src/app/util/clipboard_native.h
Normal file
36
src/app/util/clipboard_native.h
Normal file
@ -0,0 +1,36 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2016 David Capello
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
|
||||
#ifndef APP_UTIL_CLIPBOARD_NATIVE_H_INCLUDED
|
||||
#define APP_UTIL_CLIPBOARD_NATIVE_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "gfx/fwd.h"
|
||||
|
||||
namespace doc {
|
||||
class Image;
|
||||
class Mask;
|
||||
class Palette;
|
||||
}
|
||||
|
||||
namespace app {
|
||||
namespace clipboard {
|
||||
|
||||
void register_native_clipboard_formats();
|
||||
bool has_native_clipboard_bitmap();
|
||||
bool set_native_clipboard_bitmap(const doc::Image* image,
|
||||
const doc::Mask* mask,
|
||||
const doc::Palette* palette);
|
||||
bool get_native_clipboard_bitmap(doc::Image** image,
|
||||
doc::Mask** mask,
|
||||
doc::Palette** palette);
|
||||
bool get_native_clipboard_bitmap_size(gfx::Size* size);
|
||||
|
||||
} // namespace clipboard
|
||||
} // namespace app
|
||||
|
||||
#endif
|
@ -1,442 +0,0 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
|
||||
// included by clipboard.cpp
|
||||
|
||||
#include "base/serialization.h"
|
||||
#include "doc/color_scales.h"
|
||||
#include "doc/image_io.h"
|
||||
#include "doc/mask_io.h"
|
||||
#include "doc/palette_io.h"
|
||||
#include "she/display.h"
|
||||
#include "she/system.h"
|
||||
#include "ui/alert.h"
|
||||
|
||||
#ifndef LCS_WINDOWS_COLOR_SPACE
|
||||
#define LCS_WINDOWS_COLOR_SPACE 'Win '
|
||||
#endif
|
||||
|
||||
#ifndef CF_DIBV5
|
||||
#define CF_DIBV5 17
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace doc;
|
||||
using namespace base::serialization;
|
||||
using namespace base::serialization::little_endian;
|
||||
|
||||
static UINT custom_clipboard_format = 0;
|
||||
|
||||
static uint32_t get_shift_from_mask(uint32_t mask)
|
||||
{
|
||||
uint32_t shift = 0;
|
||||
for (shift=0; shift<32; ++shift)
|
||||
if (mask & (1 << shift))
|
||||
return shift;
|
||||
return shift;
|
||||
}
|
||||
|
||||
bool win32_open_clipboard(HWND hwnd)
|
||||
{
|
||||
if (!custom_clipboard_format) {
|
||||
custom_clipboard_format = RegisterClipboardFormat(L"Aseprite.Image.1");
|
||||
if (!custom_clipboard_format)
|
||||
LOG("Error registering custom clipboard format: %d\n", GetLastError());
|
||||
}
|
||||
|
||||
for (int i=0; i<5; ++i) {
|
||||
if (OpenClipboard(hwnd))
|
||||
return true;
|
||||
|
||||
Sleep(100);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the Windows clipboard contains a bitmap (CF_DIB
|
||||
* format).
|
||||
*/
|
||||
static bool win32_clipboard_contains_bitmap()
|
||||
{
|
||||
return IsClipboardFormatAvailable(CF_DIB) ? true: false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the Windows clipboard content to the specified image. The
|
||||
* palette is optional and only used if the image is IMAGE_INDEXED type.
|
||||
*/
|
||||
static void set_win32_clipboard_bitmap(const Image* image, const Mask* mask, Palette* palette)
|
||||
{
|
||||
HWND hwnd = static_cast<HWND>(she::instance()->defaultDisplay()->nativeHandle());
|
||||
if (!win32_open_clipboard(hwnd))
|
||||
return;
|
||||
|
||||
if (!EmptyClipboard()) {
|
||||
CloseClipboard();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!image) {
|
||||
CloseClipboard();
|
||||
return;
|
||||
}
|
||||
|
||||
// Set custom clipboard formats
|
||||
if (custom_clipboard_format) {
|
||||
std::stringstream os;
|
||||
write32(os,
|
||||
(image ? 1: 0) |
|
||||
(mask ? 2: 0) |
|
||||
(palette ? 4: 0));
|
||||
if (image) doc::write_image(os, image);
|
||||
if (mask) doc::write_mask(os, mask);
|
||||
if (palette) doc::write_palette(os, palette);
|
||||
|
||||
if (os.good()) {
|
||||
size_t size = (size_t)os.tellp();
|
||||
HGLOBAL hmem = GlobalAlloc(GHND, size+4);
|
||||
if (hmem) {
|
||||
char* p = (char*)GlobalLock(hmem);
|
||||
if (p) {
|
||||
*((uint32_t*)p) = size;
|
||||
os.seekp(0);
|
||||
os.read(p+4, size);
|
||||
}
|
||||
GlobalUnlock(hmem);
|
||||
if (p)
|
||||
SetClipboardData(custom_clipboard_format, hmem);
|
||||
GlobalFree(hmem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// information to create the memory necessary for the bitmap
|
||||
int padding = 0;
|
||||
int scanline = 0;
|
||||
int color_depth = 0;
|
||||
int palette_entries = 0;
|
||||
|
||||
switch (image->pixelFormat()) {
|
||||
case IMAGE_RGB:
|
||||
scanline = sizeof(uint32_t) * image->width();
|
||||
color_depth = 32;
|
||||
break;
|
||||
case IMAGE_GRAYSCALE:
|
||||
// this is right! Grayscaled is copied as RGBA in Win32 Clipboard
|
||||
scanline = sizeof(uint32_t) * image->width();
|
||||
color_depth = 32;
|
||||
break;
|
||||
case IMAGE_INDEXED:
|
||||
ASSERT(palette);
|
||||
padding = (4-(image->width()&3))&3;
|
||||
scanline = sizeof(uint8_t) * image->width();
|
||||
scanline += padding;
|
||||
color_depth = 8;
|
||||
palette_entries = palette->size();
|
||||
break;
|
||||
}
|
||||
ASSERT(scanline > 0 && color_depth > 0);
|
||||
|
||||
// create the BITMAPV5HEADER structure
|
||||
HGLOBAL hmem = GlobalAlloc(GHND,
|
||||
sizeof(BITMAPV5HEADER)
|
||||
+ palette_entries*sizeof(RGBQUAD)
|
||||
+ scanline*image->height());
|
||||
if (!hmem) {
|
||||
// TODO cannot copy exception
|
||||
CloseClipboard();
|
||||
return;
|
||||
}
|
||||
|
||||
BITMAPV5HEADER* bi = (BITMAPV5HEADER*)GlobalLock(hmem);
|
||||
|
||||
bi->bV5Size = sizeof(BITMAPV5HEADER);
|
||||
bi->bV5Width = image->width();
|
||||
bi->bV5Height = image->height();
|
||||
bi->bV5Planes = 1;
|
||||
bi->bV5BitCount = color_depth;
|
||||
bi->bV5Compression = BI_RGB;
|
||||
bi->bV5SizeImage = scanline*image->height();
|
||||
bi->bV5RedMask = 0x00ff0000;
|
||||
bi->bV5GreenMask = 0x0000ff00;
|
||||
bi->bV5BlueMask = 0x000000ff;
|
||||
bi->bV5AlphaMask = 0xff000000;
|
||||
bi->bV5CSType = LCS_WINDOWS_COLOR_SPACE;
|
||||
bi->bV5Intent = LCS_GM_GRAPHICS;
|
||||
bi->bV5ClrUsed = palette_entries == 256 ? 0: palette_entries;
|
||||
|
||||
// write pixels
|
||||
switch (image->pixelFormat()) {
|
||||
case IMAGE_RGB: {
|
||||
uint32_t* dst = (uint32_t*)(((uint8_t*)bi)+bi->bV5Size);
|
||||
uint32_t c;
|
||||
for (int y=image->height()-1; y>=0; --y)
|
||||
for (int x=0; x<image->width(); ++x) {
|
||||
c = get_pixel_fast<RgbTraits>(image, x, y);
|
||||
*(dst++) = ((rgba_getb(c) << 0) |
|
||||
(rgba_getg(c) << 8) |
|
||||
(rgba_getr(c) << 16) |
|
||||
(rgba_geta(c) << 24));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IMAGE_GRAYSCALE: {
|
||||
uint32_t* dst = (uint32_t*)(((uint8_t*)bi)+bi->bV5Size);
|
||||
uint16_t c;
|
||||
for (int y=image->height()-1; y>=0; --y)
|
||||
for (int x=0; x<image->width(); ++x) {
|
||||
c = get_pixel_fast<GrayscaleTraits>(image, x, y);
|
||||
*(dst++) = ((graya_getv(c) << 0) |
|
||||
(graya_getv(c) << 8) |
|
||||
(graya_getv(c) << 16) |
|
||||
(graya_geta(c) << 24));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IMAGE_INDEXED: {
|
||||
ASSERT(palette);
|
||||
RGBQUAD* rgbquad = (RGBQUAD*)(((uint8_t*)bi)+bi->bV5Size);
|
||||
for (int i=0; i<palette->size(); ++i) {
|
||||
rgbquad->rgbRed = rgba_getr(palette->getEntry(i));
|
||||
rgbquad->rgbGreen = rgba_getg(palette->getEntry(i));
|
||||
rgbquad->rgbBlue = rgba_getb(palette->getEntry(i));
|
||||
rgbquad++;
|
||||
}
|
||||
|
||||
uint8_t* dst = (uint8_t*)(((uint8_t*)bi)+bi->bV5Size
|
||||
+ palette_entries*sizeof(RGBQUAD));
|
||||
for (int y=image->height()-1; y>=0; --y) {
|
||||
for (int x=0; x<image->width(); ++x) {
|
||||
*(dst++) = get_pixel_fast<IndexedTraits>(image, x, y);
|
||||
}
|
||||
dst += padding;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
GlobalUnlock(hmem);
|
||||
SetClipboardData(CF_DIBV5, hmem);
|
||||
CloseClipboard();
|
||||
|
||||
GlobalFree(hmem);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an Image from the current Windows Clipboard content.
|
||||
*/
|
||||
static void get_win32_clipboard_bitmap(Image*& image, Mask*& mask, Palette*& palette)
|
||||
{
|
||||
image = nullptr;
|
||||
mask = nullptr;
|
||||
palette = nullptr;
|
||||
|
||||
if (!win32_clipboard_contains_bitmap())
|
||||
return;
|
||||
|
||||
HWND hwnd = static_cast<HWND>(she::instance()->defaultDisplay()->nativeHandle());
|
||||
if (!win32_open_clipboard(hwnd))
|
||||
return;
|
||||
|
||||
// Prefer the custom format (to avoid losing data)
|
||||
if (custom_clipboard_format &&
|
||||
IsClipboardFormatAvailable(custom_clipboard_format)) {
|
||||
const char* ptr = (const char*)GetClipboardData(custom_clipboard_format);
|
||||
if (ptr) {
|
||||
size_t size = *((uint32_t*)ptr);
|
||||
if (size > 0) {
|
||||
std::stringstream is;
|
||||
is.write(ptr+4, size);
|
||||
|
||||
int bits = read32(is);
|
||||
if (bits & 1) image = doc::read_image(is, false);
|
||||
if (bits & 2) mask = doc::read_mask(is);
|
||||
if (bits & 4) palette = doc::read_palette(is);
|
||||
}
|
||||
|
||||
CloseClipboard();
|
||||
if (image)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
BITMAPINFO* bi = (BITMAPINFO*)GetClipboardData(CF_DIB);
|
||||
if (bi) {
|
||||
if (bi->bmiHeader.biCompression != BI_RGB &&
|
||||
bi->bmiHeader.biCompression != BI_BITFIELDS) {
|
||||
ui::Alert::show("Error<<The current Windows clipboard format is not a bitmap.||&OK");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
image = Image::create(bi->bmiHeader.biBitCount == 8 ? IMAGE_INDEXED:
|
||||
IMAGE_RGB,
|
||||
bi->bmiHeader.biWidth,
|
||||
ABS(bi->bmiHeader.biHeight));
|
||||
|
||||
bool valid_image = false;
|
||||
switch (bi->bmiHeader.biBitCount) {
|
||||
|
||||
// 32 BPP
|
||||
case 32:
|
||||
if (bi->bmiHeader.biCompression == BI_BITFIELDS) {
|
||||
uint32_t* src = (uint32_t*)(((uint8_t*)bi)+bi->bmiHeader.biSize+sizeof(RGBQUAD)*3);
|
||||
uint32_t c;
|
||||
|
||||
uint32_t r_mask = (uint32_t)*((uint32_t*)&bi->bmiColors[0]);
|
||||
uint32_t g_mask = (uint32_t)*((uint32_t*)&bi->bmiColors[1]);
|
||||
uint32_t b_mask = (uint32_t)*((uint32_t*)&bi->bmiColors[2]);
|
||||
uint32_t r_shift = get_shift_from_mask(r_mask);
|
||||
uint32_t g_shift = get_shift_from_mask(g_mask);
|
||||
uint32_t b_shift = get_shift_from_mask(b_mask);
|
||||
|
||||
for (int y=image->height()-1; y>=0; --y) {
|
||||
uint32_t* dst = (uint32_t*)image->getPixelAddress(0, y);
|
||||
|
||||
for (int x=0; x<image->width(); ++x) {
|
||||
c = *(src++);
|
||||
*(dst++) = rgba((c & r_mask) >> r_shift,
|
||||
(c & g_mask) >> g_shift,
|
||||
(c & b_mask) >> b_shift, 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (bi->bmiHeader.biCompression == BI_RGB) {
|
||||
uint32_t* src = (uint32_t*)(((uint8_t*)bi)+bi->bmiHeader.biSize);
|
||||
uint32_t c;
|
||||
|
||||
for (int y=image->height()-1; y>=0; --y) {
|
||||
uint32_t* dst = (uint32_t*)image->getPixelAddress(0, y);
|
||||
|
||||
for (int x=0; x<image->width(); ++x) {
|
||||
c = *(src++);
|
||||
*(dst++) = rgba((c & 0x00ff0000) >> 16,
|
||||
(c & 0x0000ff00) >> 8,
|
||||
(c & 0x000000ff) >> 0,
|
||||
(c & 0xff000000) >> 24);
|
||||
}
|
||||
}
|
||||
}
|
||||
valid_image = true;
|
||||
break;
|
||||
|
||||
// 24 BPP
|
||||
case 24: {
|
||||
uint8_t* src = (((uint8_t*)bi)+bi->bmiHeader.biSize);
|
||||
uint8_t r, g, b;
|
||||
int padding = (4-((image->width()*3)&3))&3;
|
||||
|
||||
for (int y=image->height()-1; y>=0; --y) {
|
||||
uint32_t* dst = (uint32_t*)image->getPixelAddress(0, y);
|
||||
|
||||
for (int x=0; x<image->width(); ++x) {
|
||||
b = *(src++);
|
||||
g = *(src++);
|
||||
r = *(src++);
|
||||
*(dst++) = rgba(r, g, b, 255);
|
||||
}
|
||||
src += padding;
|
||||
}
|
||||
valid_image = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// 16 BPP
|
||||
case 16: {
|
||||
uint16_t* src = (uint16_t*)(((uint8_t*)bi)+bi->bmiHeader.biSize+sizeof(RGBQUAD)*3);
|
||||
uint16_t word;
|
||||
uint8_t r, g, b;
|
||||
int padding = ((4-((image->width()*2)&3))&3)/2;
|
||||
|
||||
uint32_t r_mask = (uint32_t)*((uint32_t*)&bi->bmiColors[0]);
|
||||
uint32_t g_mask = (uint32_t)*((uint32_t*)&bi->bmiColors[1]);
|
||||
uint32_t b_mask = (uint32_t)*((uint32_t*)&bi->bmiColors[2]);
|
||||
uint32_t r_shift = get_shift_from_mask(r_mask);
|
||||
uint32_t g_shift = get_shift_from_mask(g_mask);
|
||||
uint32_t b_shift = get_shift_from_mask(b_mask);
|
||||
|
||||
for (int y=image->height()-1; y>=0; --y) {
|
||||
for (int x=0; x<image->width(); ++x) {
|
||||
word = *(src++);
|
||||
r = scale_5bits_to_8bits((word & r_mask) >> r_shift);
|
||||
g = scale_6bits_to_8bits((word & g_mask) >> g_shift);
|
||||
b = scale_5bits_to_8bits((word & b_mask) >> b_shift);
|
||||
put_pixel_fast<RgbTraits>(image, x, y, rgba(r, g, b, 255));
|
||||
}
|
||||
src += padding;
|
||||
}
|
||||
valid_image = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// 8 BPP
|
||||
case 8: {
|
||||
int colors = bi->bmiHeader.biClrUsed > 0 ? bi->bmiHeader.biClrUsed: 256;
|
||||
palette = new Palette(frame_t(0), colors);
|
||||
for (int c=0; c<colors; ++c) {
|
||||
palette->setEntry(c, rgba(bi->bmiColors[c].rgbRed,
|
||||
bi->bmiColors[c].rgbGreen,
|
||||
bi->bmiColors[c].rgbBlue, 255));
|
||||
}
|
||||
|
||||
uint8_t* src = (((uint8_t*)bi)+bi->bmiHeader.biSize+sizeof(RGBQUAD)*colors);
|
||||
int padding = (4-(image->width()&3))&3;
|
||||
|
||||
for (int y=image->height()-1; y>=0; --y) {
|
||||
for (int x=0; x<image->width(); ++x) {
|
||||
int c = *(src++);
|
||||
put_pixel_fast<IndexedTraits>(image, x, y, MID(0, c, colors-1));
|
||||
}
|
||||
|
||||
src += padding;
|
||||
}
|
||||
|
||||
valid_image = true;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!valid_image) {
|
||||
delete image;
|
||||
delete palette;
|
||||
image = NULL;
|
||||
palette = NULL;
|
||||
}
|
||||
}
|
||||
catch (...) {
|
||||
delete image;
|
||||
delete palette;
|
||||
image = NULL;
|
||||
palette = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
CloseClipboard();
|
||||
}
|
||||
|
||||
static bool get_win32_clipboard_bitmap_size(gfx::Size& size)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
HWND hwnd = static_cast<HWND>(she::instance()->defaultDisplay()->nativeHandle());
|
||||
|
||||
if (win32_clipboard_contains_bitmap() && win32_open_clipboard(hwnd)) {
|
||||
BITMAPINFO* bi = (BITMAPINFO*)GetClipboardData(CF_DIB);
|
||||
if (bi) {
|
||||
size.w = bi->bmiHeader.biWidth;
|
||||
size.h = ABS(bi->bmiHeader.biHeight);
|
||||
result = true;
|
||||
}
|
||||
CloseClipboard();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
2
src/clip
2
src/clip
@ -1 +1 @@
|
||||
Subproject commit e57130a56659d001d7bc33081c7cb3abe0621be2
|
||||
Subproject commit 5ef7852ae4d297aa5ab14ad5e5728e1f0c2e50bd
|
Loading…
x
Reference in New Issue
Block a user