mirror of
https://github.com/QB64-Phoenix-Edition/QB64pe.git
synced 2024-09-20 09:04:43 +00:00
660 lines
18 KiB
C++
660 lines
18 KiB
C++
|
// Clip Library
|
||
|
// Copyright (C) 2015-2020 David Capello
|
||
|
//
|
||
|
// This file is released under the terms of the MIT license.
|
||
|
// Read LICENSE.txt for more information.
|
||
|
|
||
|
#include "clip.h"
|
||
|
#include "clip_common.h"
|
||
|
#include "clip_lock_impl.h"
|
||
|
|
||
|
#include <algorithm>
|
||
|
#include <cstdlib>
|
||
|
#include <cstring>
|
||
|
#include <string>
|
||
|
#include <vector>
|
||
|
|
||
|
#include <windows.h>
|
||
|
|
||
|
#if CLIP_ENABLE_IMAGE
|
||
|
#include "clip_win_wic.h"
|
||
|
#endif // CLIP_ENABLE_IMAGE
|
||
|
|
||
|
#ifndef LCS_WINDOWS_COLOR_SPACE
|
||
|
#define LCS_WINDOWS_COLOR_SPACE 'Win '
|
||
|
#endif
|
||
|
|
||
|
#ifndef CF_DIBV5
|
||
|
#define CF_DIBV5 17
|
||
|
#endif
|
||
|
|
||
|
namespace clip {
|
||
|
|
||
|
namespace {
|
||
|
|
||
|
// Data type used as header for custom formats to indicate the exact
|
||
|
// size of the user custom data. This is necessary because it looks
|
||
|
// like GlobalSize() might not return the exact size, but a greater
|
||
|
// value.
|
||
|
typedef uint64_t CustomSizeT;
|
||
|
|
||
|
unsigned long get_shift_from_mask(unsigned long mask) {
|
||
|
unsigned long shift = 0;
|
||
|
for (shift=0; shift<sizeof(unsigned long)*8; ++shift)
|
||
|
if (mask & (1 << shift))
|
||
|
return shift;
|
||
|
return shift;
|
||
|
}
|
||
|
|
||
|
class Hglobal {
|
||
|
public:
|
||
|
Hglobal() : m_handle(nullptr) {
|
||
|
}
|
||
|
|
||
|
explicit Hglobal(HGLOBAL handle) : m_handle(handle) {
|
||
|
}
|
||
|
|
||
|
explicit Hglobal(size_t len) : m_handle(GlobalAlloc(GHND, len)) {
|
||
|
}
|
||
|
|
||
|
~Hglobal() {
|
||
|
if (m_handle)
|
||
|
GlobalFree(m_handle);
|
||
|
}
|
||
|
|
||
|
void release() {
|
||
|
m_handle = nullptr;
|
||
|
}
|
||
|
|
||
|
operator HGLOBAL() {
|
||
|
return m_handle;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
HGLOBAL m_handle;
|
||
|
};
|
||
|
|
||
|
#if CLIP_ENABLE_IMAGE
|
||
|
|
||
|
struct BitmapInfo {
|
||
|
BITMAPV5HEADER* b5 = nullptr;
|
||
|
BITMAPINFO* bi = nullptr;
|
||
|
int width = 0;
|
||
|
int height = 0;
|
||
|
uint16_t bit_count = 0;
|
||
|
uint32_t compression = 0;
|
||
|
uint32_t red_mask = 0;
|
||
|
uint32_t green_mask = 0;
|
||
|
uint32_t blue_mask = 0;
|
||
|
uint32_t alpha_mask = 0;
|
||
|
|
||
|
BitmapInfo() {
|
||
|
// Use DIBV5 only for 32 bpp uncompressed bitmaps and when all
|
||
|
// masks are valid.
|
||
|
if (IsClipboardFormatAvailable(CF_DIBV5)) {
|
||
|
b5 = (BITMAPV5HEADER*)GetClipboardData(CF_DIBV5);
|
||
|
if (b5 &&
|
||
|
b5->bV5BitCount == 32 &&
|
||
|
((b5->bV5Compression == BI_RGB) ||
|
||
|
(b5->bV5Compression == BI_BITFIELDS &&
|
||
|
b5->bV5RedMask && b5->bV5GreenMask &&
|
||
|
b5->bV5BlueMask && b5->bV5AlphaMask))) {
|
||
|
width = b5->bV5Width;
|
||
|
height = b5->bV5Height;
|
||
|
bit_count = b5->bV5BitCount;
|
||
|
compression = b5->bV5Compression;
|
||
|
if (compression == BI_BITFIELDS) {
|
||
|
red_mask = b5->bV5RedMask;
|
||
|
green_mask = b5->bV5GreenMask;
|
||
|
blue_mask = b5->bV5BlueMask;
|
||
|
alpha_mask = b5->bV5AlphaMask;
|
||
|
}
|
||
|
else {
|
||
|
red_mask = 0xff0000;
|
||
|
green_mask = 0xff00;
|
||
|
blue_mask = 0xff;
|
||
|
alpha_mask = 0xff000000;
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (IsClipboardFormatAvailable(CF_DIB))
|
||
|
bi = (BITMAPINFO*)GetClipboardData(CF_DIB);
|
||
|
if (!bi)
|
||
|
return;
|
||
|
|
||
|
width = bi->bmiHeader.biWidth;
|
||
|
height = bi->bmiHeader.biHeight;
|
||
|
bit_count = bi->bmiHeader.biBitCount;
|
||
|
compression = bi->bmiHeader.biCompression;
|
||
|
|
||
|
if (compression == BI_BITFIELDS) {
|
||
|
red_mask = *((uint32_t*)&bi->bmiColors[0]);
|
||
|
green_mask = *((uint32_t*)&bi->bmiColors[1]);
|
||
|
blue_mask = *((uint32_t*)&bi->bmiColors[2]);
|
||
|
if (bit_count == 32)
|
||
|
alpha_mask = 0xff000000;
|
||
|
}
|
||
|
else if (compression == BI_RGB) {
|
||
|
switch (bit_count) {
|
||
|
case 32:
|
||
|
red_mask = 0xff0000;
|
||
|
green_mask = 0xff00;
|
||
|
blue_mask = 0xff;
|
||
|
alpha_mask = 0xff000000;
|
||
|
break;
|
||
|
case 24:
|
||
|
case 8: // We return 8bpp images as 24bpp
|
||
|
red_mask = 0xff0000;
|
||
|
green_mask = 0xff00;
|
||
|
blue_mask = 0xff;
|
||
|
break;
|
||
|
case 16:
|
||
|
red_mask = 0x7c00;
|
||
|
green_mask = 0x03e0;
|
||
|
blue_mask = 0x001f;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool is_valid() const {
|
||
|
return (b5 || bi);
|
||
|
}
|
||
|
|
||
|
void fill_spec(image_spec& spec) {
|
||
|
spec.width = width;
|
||
|
spec.height = (height >= 0 ? height: -height);
|
||
|
// We convert indexed to 24bpp RGB images to match the OS X behavior
|
||
|
spec.bits_per_pixel = bit_count;
|
||
|
if (spec.bits_per_pixel <= 8)
|
||
|
spec.bits_per_pixel = 24;
|
||
|
spec.bytes_per_row = width*((spec.bits_per_pixel+7)/8);
|
||
|
spec.red_mask = red_mask;
|
||
|
spec.green_mask = green_mask;
|
||
|
spec.blue_mask = blue_mask;
|
||
|
spec.alpha_mask = alpha_mask;
|
||
|
|
||
|
switch (spec.bits_per_pixel) {
|
||
|
|
||
|
case 24: {
|
||
|
// We need one extra byte to avoid a crash updating the last
|
||
|
// pixel on last row using:
|
||
|
//
|
||
|
// *((uint32_t*)ptr) = pixel24bpp;
|
||
|
//
|
||
|
++spec.bytes_per_row;
|
||
|
|
||
|
// Align each row to 32bpp
|
||
|
int padding = (4-(spec.bytes_per_row&3))&3;
|
||
|
spec.bytes_per_row += padding;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case 16: {
|
||
|
int padding = (4-(spec.bytes_per_row&3))&3;
|
||
|
spec.bytes_per_row += padding;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
unsigned long* masks = &spec.red_mask;
|
||
|
unsigned long* shifts = &spec.red_shift;
|
||
|
for (unsigned long* shift=shifts, *mask=masks; shift<shifts+4; ++shift, ++mask) {
|
||
|
if (*mask)
|
||
|
*shift = get_shift_from_mask(*mask);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
};
|
||
|
|
||
|
#endif // CLIP_ENABLE_IMAGE
|
||
|
|
||
|
}
|
||
|
|
||
|
lock::impl::impl(void* hwnd) : m_locked(false) {
|
||
|
for (int i=0; i<5; ++i) {
|
||
|
if (OpenClipboard((HWND)hwnd)) {
|
||
|
m_locked = true;
|
||
|
break;
|
||
|
}
|
||
|
Sleep(20);
|
||
|
}
|
||
|
|
||
|
if (!m_locked) {
|
||
|
error_handler e = get_error_handler();
|
||
|
if (e)
|
||
|
e(ErrorCode::CannotLock);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
lock::impl::~impl() {
|
||
|
if (m_locked)
|
||
|
CloseClipboard();
|
||
|
}
|
||
|
|
||
|
bool lock::impl::clear() {
|
||
|
return (EmptyClipboard() ? true: false);
|
||
|
}
|
||
|
|
||
|
bool lock::impl::is_convertible(format f) const {
|
||
|
if (f == text_format()) {
|
||
|
return
|
||
|
(IsClipboardFormatAvailable(CF_TEXT) ||
|
||
|
IsClipboardFormatAvailable(CF_UNICODETEXT) ||
|
||
|
IsClipboardFormatAvailable(CF_OEMTEXT));
|
||
|
}
|
||
|
#if CLIP_ENABLE_IMAGE
|
||
|
else if (f == image_format()) {
|
||
|
return (IsClipboardFormatAvailable(CF_DIB) ? true: false);
|
||
|
}
|
||
|
#endif // CLIP_ENABLE_IMAGE
|
||
|
else if (IsClipboardFormatAvailable(f))
|
||
|
return true;
|
||
|
else
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool lock::impl::set_data(format f, const char* buf, size_t len) {
|
||
|
bool result = false;
|
||
|
|
||
|
if (f == text_format()) {
|
||
|
if (len > 0) {
|
||
|
int reqsize = MultiByteToWideChar(CP_UTF8, 0, buf, len, NULL, 0);
|
||
|
if (reqsize > 0) {
|
||
|
++reqsize;
|
||
|
|
||
|
Hglobal hglobal(sizeof(WCHAR)*reqsize);
|
||
|
LPWSTR lpstr = static_cast<LPWSTR>(GlobalLock(hglobal));
|
||
|
MultiByteToWideChar(CP_UTF8, 0, buf, len, lpstr, reqsize);
|
||
|
GlobalUnlock(hglobal);
|
||
|
|
||
|
result = (SetClipboardData(CF_UNICODETEXT, hglobal)) ? true: false;
|
||
|
if (result)
|
||
|
hglobal.release();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
Hglobal hglobal(len+sizeof(CustomSizeT));
|
||
|
if (hglobal) {
|
||
|
auto dst = (uint8_t*)GlobalLock(hglobal);
|
||
|
if (dst) {
|
||
|
*((CustomSizeT*)dst) = len;
|
||
|
memcpy(dst+sizeof(CustomSizeT), buf, len);
|
||
|
GlobalUnlock(hglobal);
|
||
|
result = (SetClipboardData(f, hglobal) ? true: false);
|
||
|
if (result)
|
||
|
hglobal.release();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
bool lock::impl::get_data(format f, char* buf, size_t len) const {
|
||
|
assert(buf);
|
||
|
|
||
|
if (!buf || !is_convertible(f))
|
||
|
return false;
|
||
|
|
||
|
bool result = false;
|
||
|
|
||
|
if (f == text_format()) {
|
||
|
if (IsClipboardFormatAvailable(CF_UNICODETEXT)) {
|
||
|
HGLOBAL hglobal = GetClipboardData(CF_UNICODETEXT);
|
||
|
if (hglobal) {
|
||
|
LPWSTR lpstr = static_cast<LPWSTR>(GlobalLock(hglobal));
|
||
|
if (lpstr) {
|
||
|
size_t reqsize =
|
||
|
WideCharToMultiByte(CP_UTF8, 0, lpstr, -1,
|
||
|
nullptr, 0, nullptr, nullptr);
|
||
|
|
||
|
assert(reqsize <= len);
|
||
|
if (reqsize <= len) {
|
||
|
WideCharToMultiByte(CP_UTF8, 0, lpstr, -1,
|
||
|
buf, reqsize, nullptr, nullptr);
|
||
|
result = true;
|
||
|
}
|
||
|
GlobalUnlock(hglobal);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else if (IsClipboardFormatAvailable(CF_TEXT)) {
|
||
|
HGLOBAL hglobal = GetClipboardData(CF_TEXT);
|
||
|
if (hglobal) {
|
||
|
LPSTR lpstr = static_cast<LPSTR>(GlobalLock(hglobal));
|
||
|
if (lpstr) {
|
||
|
// TODO check length
|
||
|
memcpy(buf, lpstr, len);
|
||
|
result = true;
|
||
|
GlobalUnlock(hglobal);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
if (IsClipboardFormatAvailable(f)) {
|
||
|
HGLOBAL hglobal = GetClipboardData(f);
|
||
|
if (hglobal) {
|
||
|
const SIZE_T total_size = GlobalSize(hglobal);
|
||
|
auto ptr = (const uint8_t*)GlobalLock(hglobal);
|
||
|
if (ptr) {
|
||
|
CustomSizeT reqsize = *((CustomSizeT*)ptr);
|
||
|
|
||
|
// If the registered length of data in the first CustomSizeT
|
||
|
// number of bytes of the hglobal data is greater than the
|
||
|
// GlobalSize(hglobal), something is wrong, it should not
|
||
|
// happen.
|
||
|
assert(reqsize <= total_size);
|
||
|
if (reqsize > total_size)
|
||
|
reqsize = total_size - sizeof(CustomSizeT);
|
||
|
|
||
|
if (reqsize <= len) {
|
||
|
memcpy(buf, ptr+sizeof(CustomSizeT), reqsize);
|
||
|
result = true;
|
||
|
}
|
||
|
GlobalUnlock(hglobal);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
size_t lock::impl::get_data_length(format f) const {
|
||
|
size_t len = 0;
|
||
|
|
||
|
if (f == text_format()) {
|
||
|
if (IsClipboardFormatAvailable(CF_UNICODETEXT)) {
|
||
|
HGLOBAL hglobal = GetClipboardData(CF_UNICODETEXT);
|
||
|
if (hglobal) {
|
||
|
LPWSTR lpstr = static_cast<LPWSTR>(GlobalLock(hglobal));
|
||
|
if (lpstr) {
|
||
|
len =
|
||
|
WideCharToMultiByte(CP_UTF8, 0, lpstr, -1,
|
||
|
nullptr, 0, nullptr, nullptr);
|
||
|
GlobalUnlock(hglobal);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else if (IsClipboardFormatAvailable(CF_TEXT)) {
|
||
|
HGLOBAL hglobal = GetClipboardData(CF_TEXT);
|
||
|
if (hglobal) {
|
||
|
LPSTR lpstr = (LPSTR)GlobalLock(hglobal);
|
||
|
if (lpstr) {
|
||
|
len = strlen(lpstr) + 1;
|
||
|
GlobalUnlock(hglobal);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else if (f != empty_format()) {
|
||
|
if (IsClipboardFormatAvailable(f)) {
|
||
|
HGLOBAL hglobal = GetClipboardData(f);
|
||
|
if (hglobal) {
|
||
|
const SIZE_T total_size = GlobalSize(hglobal);
|
||
|
auto ptr = (const uint8_t*)GlobalLock(hglobal);
|
||
|
if (ptr) {
|
||
|
len = *((CustomSizeT*)ptr);
|
||
|
|
||
|
assert(len <= total_size);
|
||
|
if (len > total_size)
|
||
|
len = total_size - sizeof(CustomSizeT);
|
||
|
|
||
|
GlobalUnlock(hglobal);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return len;
|
||
|
}
|
||
|
|
||
|
#if CLIP_ENABLE_IMAGE
|
||
|
|
||
|
bool lock::impl::set_image(const image& image) {
|
||
|
const image_spec& spec = image.spec();
|
||
|
|
||
|
// Add the PNG clipboard format for images with alpha channel
|
||
|
// (useful to communicate with some Windows programs that only use
|
||
|
// alpha data from PNG clipboard format)
|
||
|
if (spec.bits_per_pixel == 32 &&
|
||
|
spec.alpha_mask) {
|
||
|
UINT png_format = RegisterClipboardFormatA("PNG");
|
||
|
if (png_format) {
|
||
|
Hglobal png_handle(win::write_png(image));
|
||
|
if (png_handle)
|
||
|
SetClipboardData(png_format, png_handle);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
image_spec out_spec = spec;
|
||
|
|
||
|
int palette_colors = 0;
|
||
|
int padding = 0;
|
||
|
switch (spec.bits_per_pixel) {
|
||
|
case 24: padding = (4-((spec.width*3)&3))&3; break;
|
||
|
case 16: padding = ((4-((spec.width*2)&3))&3)/2; break;
|
||
|
case 8: padding = (4-(spec.width&3))&3; break;
|
||
|
}
|
||
|
out_spec.bytes_per_row += padding;
|
||
|
|
||
|
// Create the BITMAPV5HEADER structure
|
||
|
Hglobal hmem(
|
||
|
GlobalAlloc(
|
||
|
GHND,
|
||
|
sizeof(BITMAPV5HEADER)
|
||
|
+ palette_colors*sizeof(RGBQUAD)
|
||
|
+ out_spec.bytes_per_row*out_spec.height));
|
||
|
if (!hmem)
|
||
|
return false;
|
||
|
|
||
|
out_spec.red_mask = 0x00ff0000;
|
||
|
out_spec.green_mask = 0xff00;
|
||
|
out_spec.blue_mask = 0xff;
|
||
|
out_spec.alpha_mask = 0xff000000;
|
||
|
out_spec.red_shift = 16;
|
||
|
out_spec.green_shift = 8;
|
||
|
out_spec.blue_shift = 0;
|
||
|
out_spec.alpha_shift = 24;
|
||
|
|
||
|
BITMAPV5HEADER* bi = (BITMAPV5HEADER*)GlobalLock(hmem);
|
||
|
bi->bV5Size = sizeof(BITMAPV5HEADER);
|
||
|
bi->bV5Width = out_spec.width;
|
||
|
bi->bV5Height = out_spec.height;
|
||
|
bi->bV5Planes = 1;
|
||
|
bi->bV5BitCount = (WORD)out_spec.bits_per_pixel;
|
||
|
bi->bV5Compression = BI_RGB;
|
||
|
bi->bV5SizeImage = out_spec.bytes_per_row*spec.height;
|
||
|
bi->bV5RedMask = out_spec.red_mask;
|
||
|
bi->bV5GreenMask = out_spec.green_mask;
|
||
|
bi->bV5BlueMask = out_spec.blue_mask;
|
||
|
bi->bV5AlphaMask = out_spec.alpha_mask;
|
||
|
bi->bV5CSType = LCS_WINDOWS_COLOR_SPACE;
|
||
|
bi->bV5Intent = LCS_GM_GRAPHICS;
|
||
|
bi->bV5ClrUsed = 0;
|
||
|
|
||
|
switch (spec.bits_per_pixel) {
|
||
|
case 32: {
|
||
|
const char* src = image.data();
|
||
|
char* dst = (((char*)bi)+bi->bV5Size) + (out_spec.height-1)*out_spec.bytes_per_row;
|
||
|
for (long y=spec.height-1; y>=0; --y) {
|
||
|
const uint32_t* src_x = (const uint32_t*)src;
|
||
|
uint32_t* dst_x = (uint32_t*)dst;
|
||
|
|
||
|
for (unsigned long x=0; x<spec.width; ++x, ++src_x, ++dst_x) {
|
||
|
uint32_t c = *src_x;
|
||
|
int r = ((c & spec.red_mask ) >> spec.red_shift );
|
||
|
int g = ((c & spec.green_mask) >> spec.green_shift);
|
||
|
int b = ((c & spec.blue_mask ) >> spec.blue_shift );
|
||
|
int a = ((c & spec.alpha_mask) >> spec.alpha_shift);
|
||
|
|
||
|
// Windows requires premultiplied RGBA values
|
||
|
r = r * a / 255;
|
||
|
g = g * a / 255;
|
||
|
b = b * a / 255;
|
||
|
|
||
|
*dst_x =
|
||
|
(r << out_spec.red_shift ) |
|
||
|
(g << out_spec.green_shift) |
|
||
|
(b << out_spec.blue_shift ) |
|
||
|
(a << out_spec.alpha_shift);
|
||
|
}
|
||
|
|
||
|
src += spec.bytes_per_row;
|
||
|
dst -= out_spec.bytes_per_row;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
default:
|
||
|
error_handler e = get_error_handler();
|
||
|
if (e)
|
||
|
e(ErrorCode::ImageNotSupported);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
GlobalUnlock(hmem);
|
||
|
SetClipboardData(CF_DIBV5, hmem);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool lock::impl::get_image(image& output_img) const {
|
||
|
// Get the "PNG" clipboard format (this is useful only for 32bpp
|
||
|
// images with alpha channel, in other case we can use the regular
|
||
|
// DIB format)
|
||
|
UINT png_format = RegisterClipboardFormatA("PNG");
|
||
|
if (png_format && IsClipboardFormatAvailable(png_format)) {
|
||
|
HANDLE png_handle = GetClipboardData(png_format);
|
||
|
if (png_handle) {
|
||
|
size_t png_size = GlobalSize(png_handle);
|
||
|
uint8_t* png_data = (uint8_t*)GlobalLock(png_handle);
|
||
|
bool result = win::read_png(png_data, png_size, &output_img, nullptr);
|
||
|
GlobalUnlock(png_handle);
|
||
|
if (result)
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
BitmapInfo bi;
|
||
|
if (!bi.is_valid()) {
|
||
|
// There is no image at all in the clipboard, no need to report
|
||
|
// this as an error, just return false.
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
image_spec spec;
|
||
|
bi.fill_spec(spec);
|
||
|
image img(spec);
|
||
|
|
||
|
switch (bi.bit_count) {
|
||
|
|
||
|
case 32:
|
||
|
case 24:
|
||
|
case 16: {
|
||
|
const uint8_t* src = nullptr;
|
||
|
|
||
|
if (bi.compression == BI_RGB ||
|
||
|
bi.compression == BI_BITFIELDS) {
|
||
|
if (bi.b5)
|
||
|
src = ((uint8_t*)bi.b5) + bi.b5->bV5Size;
|
||
|
else
|
||
|
src = ((uint8_t*)bi.bi) + bi.bi->bmiHeader.biSize;
|
||
|
if (bi.compression == BI_BITFIELDS)
|
||
|
src += sizeof(RGBQUAD)*3;
|
||
|
}
|
||
|
|
||
|
if (src) {
|
||
|
const int src_bytes_per_row = spec.width*((bi.bit_count+7)/8);
|
||
|
const int padding = (4-(src_bytes_per_row&3))&3;
|
||
|
|
||
|
for (long y=spec.height-1; y>=0; --y, src+=src_bytes_per_row+padding) {
|
||
|
char* dst = img.data()+y*spec.bytes_per_row;
|
||
|
std::copy(src, src+src_bytes_per_row, dst);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Windows uses premultiplied RGB values, and we use straight
|
||
|
// alpha. So we have to divide all RGB values by its alpha.
|
||
|
if (bi.bit_count == 32 && spec.alpha_mask) {
|
||
|
details::divide_rgb_by_alpha(img);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case 8: {
|
||
|
assert(bi.bi);
|
||
|
|
||
|
const int colors = (bi.bi->bmiHeader.biClrUsed > 0 ? bi.bi->bmiHeader.biClrUsed: 256);
|
||
|
std::vector<uint32_t> palette(colors);
|
||
|
for (int c=0; c<colors; ++c) {
|
||
|
palette[c] =
|
||
|
(bi.bi->bmiColors[c].rgbRed << spec.red_shift) |
|
||
|
(bi.bi->bmiColors[c].rgbGreen << spec.green_shift) |
|
||
|
(bi.bi->bmiColors[c].rgbBlue << spec.blue_shift);
|
||
|
}
|
||
|
|
||
|
const uint8_t* src = (((uint8_t*)bi.bi) + bi.bi->bmiHeader.biSize + sizeof(RGBQUAD)*colors);
|
||
|
const int padding = (4-(spec.width&3))&3;
|
||
|
|
||
|
for (long y=spec.height-1; y>=0; --y, src+=padding) {
|
||
|
char* dst = img.data()+y*spec.bytes_per_row;
|
||
|
|
||
|
for (unsigned long x=0; x<spec.width; ++x, ++src, dst+=3) {
|
||
|
int idx = *src;
|
||
|
if (idx < 0)
|
||
|
idx = 0;
|
||
|
else if (idx >= colors)
|
||
|
idx = colors-1;
|
||
|
|
||
|
*((uint32_t*)dst) = palette[idx];
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
std::swap(output_img, img);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool lock::impl::get_image_spec(image_spec& spec) const {
|
||
|
UINT png_format = RegisterClipboardFormatA("PNG");
|
||
|
if (png_format && IsClipboardFormatAvailable(png_format)) {
|
||
|
HANDLE png_handle = GetClipboardData(png_format);
|
||
|
if (png_handle) {
|
||
|
size_t png_size = GlobalSize(png_handle);
|
||
|
uint8_t* png_data = (uint8_t*)GlobalLock(png_handle);
|
||
|
bool result = win::read_png(png_data, png_size, nullptr, &spec);
|
||
|
GlobalUnlock(png_handle);
|
||
|
if (result)
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
BitmapInfo bi;
|
||
|
if (!bi.is_valid())
|
||
|
return false;
|
||
|
bi.fill_spec(spec);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
#endif // CLIP_ENABLE_IMAGE
|
||
|
|
||
|
format register_format(const std::string& name) {
|
||
|
int reqsize = 1+MultiByteToWideChar(CP_UTF8, 0,
|
||
|
name.c_str(), name.size(), NULL, 0);
|
||
|
std::vector<WCHAR> buf(reqsize);
|
||
|
MultiByteToWideChar(CP_UTF8, 0, name.c_str(), name.size(),
|
||
|
&buf[0], reqsize);
|
||
|
|
||
|
// From MSDN, registered clipboard formats are identified by values
|
||
|
// in the range 0xC000 through 0xFFFF.
|
||
|
return (format)RegisterClipboardFormatW(&buf[0]);
|
||
|
}
|
||
|
|
||
|
} // namespace clip
|