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:
David Capello 2016-04-29 20:42:05 -03:00
parent 60fd6072e9
commit c99a187256
6 changed files with 383 additions and 483 deletions

View File

@ -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

View File

@ -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()

View 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

View 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

View File

@ -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;
}
}

@ -1 +1 @@
Subproject commit e57130a56659d001d7bc33081c7cb3abe0621be2
Subproject commit 5ef7852ae4d297aa5ab14ad5e5728e1f0c2e50bd