remove clip-lib
This commit is contained in:
		
							
								
								
									
										7
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
{
 | 
			
		||||
    "files.associations": {
 | 
			
		||||
        "*.pyw": "python",
 | 
			
		||||
        "string": "cpp",
 | 
			
		||||
        "ostream": "cpp"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -3,6 +3,5 @@ find_program(CMAKE_C_COMPILER NAMES $ENV{CC} gcc PATHS ENV PATH NO_DEFAULT_PATH)
 | 
			
		||||
find_program(CMAKE_CXX_COMPILER NAMES $ENV{CXX} g++ PATHS ENV PATH NO_DEFAULT_PATH)
 | 
			
		||||
project(owo)
 | 
			
		||||
add_executable(owo src/main.cpp)
 | 
			
		||||
add_subdirectory(src/clip-lib)
 | 
			
		||||
target_link_libraries(owo clip)
 | 
			
		||||
target_link_libraries(owo)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								PKGBUILD
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								PKGBUILD
									
									
									
									
									
								
							@@ -8,13 +8,13 @@ makedepends=(cmake git gcc)
 | 
			
		||||
license=('MIT')
 | 
			
		||||
 | 
			
		||||
source=("git+https://git.alfieking.dev/acetheking987/term-owo-cpp.git")
 | 
			
		||||
md5sums=('SKIP')
 | 
			
		||||
md5sums=('SKIP')    
 | 
			
		||||
 | 
			
		||||
build() {
 | 
			
		||||
    cd "$srcdir/term-owo-cpp"
 | 
			
		||||
    cmake . -B build
 | 
			
		||||
    cd build && make
 | 
			
		||||
}
 | 
			
		||||
}                               
 | 
			
		||||
 | 
			
		||||
package() {
 | 
			
		||||
    cd "$srcdir/term-owo-cpp/build"
 | 
			
		||||
 
 | 
			
		||||
@@ -1,98 +0,0 @@
 | 
			
		||||
# Clip Library
 | 
			
		||||
# Copyright (c) 2015-2024 David Capello
 | 
			
		||||
 | 
			
		||||
cmake_minimum_required(VERSION 3.5)
 | 
			
		||||
 | 
			
		||||
project(clip LANGUAGES CXX)
 | 
			
		||||
 | 
			
		||||
set(CMAKE_CXX_STANDARD 11)
 | 
			
		||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
 | 
			
		||||
 | 
			
		||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
 | 
			
		||||
  # Use libc++ explicitly so we can compile for
 | 
			
		||||
  # CMAKE_OSX_DEPLOYMENT_TARGET=10.7 or 10.8
 | 
			
		||||
  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
option(CLIP_ENABLE_IMAGE "Compile with support to copy/paste images" on)
 | 
			
		||||
if(WIN32)
 | 
			
		||||
  option(CLIP_ENABLE_LIST_FORMATS "Compile with support to list clipboard formats" off)
 | 
			
		||||
endif()
 | 
			
		||||
option(CLIP_EXAMPLES "Compile clip examples" on)
 | 
			
		||||
option(CLIP_TESTS "Compile clip tests" on)
 | 
			
		||||
if(UNIX AND NOT APPLE)
 | 
			
		||||
  option(CLIP_X11_WITH_PNG "Compile with libpng to support copy/paste image in png format" on)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
add_library(clip clip.cpp)
 | 
			
		||||
 | 
			
		||||
if(CLIP_ENABLE_IMAGE)
 | 
			
		||||
  target_sources(clip PRIVATE image.cpp)
 | 
			
		||||
  target_compile_definitions(clip PUBLIC -DCLIP_ENABLE_IMAGE=1)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
if(CLIP_ENABLE_LIST_FORMATS)
 | 
			
		||||
  target_compile_definitions(clip PUBLIC -DCLIP_ENABLE_LIST_FORMATS=1)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
if(WIN32)
 | 
			
		||||
  option(CLIP_SUPPORT_WINXP "Enable Windows XP support" OFF)
 | 
			
		||||
 | 
			
		||||
  target_sources(clip PRIVATE clip_win.cpp)
 | 
			
		||||
  if(CLIP_ENABLE_IMAGE)
 | 
			
		||||
    target_sources(clip PRIVATE clip_win_bmp.cpp clip_win_wic.cpp)
 | 
			
		||||
    target_link_libraries(clip shlwapi)
 | 
			
		||||
  endif()
 | 
			
		||||
 | 
			
		||||
  if(MSVC)
 | 
			
		||||
    target_compile_definitions(clip PRIVATE -D_SCL_SECURE_NO_WARNINGS)
 | 
			
		||||
  endif()
 | 
			
		||||
  if (CLIP_SUPPORT_WINXP)
 | 
			
		||||
    target_compile_definitions(clip PRIVATE -DCLIP_SUPPORT_WINXP)
 | 
			
		||||
  endif()
 | 
			
		||||
 | 
			
		||||
  # MinGW requires the windowscodecs just because CLSIDs are defined
 | 
			
		||||
  # in the windowscodecs.a file instead of the wincodec.h file (?!)
 | 
			
		||||
  if(MINGW)
 | 
			
		||||
    find_library(CLIP_WINDOWSCODECS_LIBRARY windowscodecs)
 | 
			
		||||
    if(CLIP_WINDOWSCODECS_LIBRARY)
 | 
			
		||||
      target_link_libraries(clip ${CLIP_WINDOWSCODECS_LIBRARY})
 | 
			
		||||
    endif()
 | 
			
		||||
  endif()
 | 
			
		||||
elseif(APPLE)
 | 
			
		||||
  target_compile_options(clip PRIVATE -fobjc-arc)
 | 
			
		||||
 | 
			
		||||
  find_library(COCOA_LIBRARY Cocoa)
 | 
			
		||||
  if(COCOA_LIBRARY)
 | 
			
		||||
    target_sources(clip PRIVATE clip_osx.mm)
 | 
			
		||||
    target_link_libraries(clip ${COCOA_LIBRARY})
 | 
			
		||||
  else()
 | 
			
		||||
    target_sources(clip PRIVATE clip_none.cpp)
 | 
			
		||||
  endif()
 | 
			
		||||
elseif(UNIX)
 | 
			
		||||
  include(CheckIncludeFiles)
 | 
			
		||||
  check_include_files(xcb/xcb.h HAVE_XCB_XLIB_H)
 | 
			
		||||
 | 
			
		||||
  if(HAVE_XCB_XLIB_H)
 | 
			
		||||
    target_compile_definitions(clip PRIVATE -DHAVE_XCB_XLIB_H)
 | 
			
		||||
    target_link_libraries(clip xcb pthread)
 | 
			
		||||
 | 
			
		||||
    if(CLIP_ENABLE_IMAGE AND CLIP_X11_WITH_PNG)
 | 
			
		||||
      check_include_files(png.h HAVE_PNG_H)
 | 
			
		||||
      if(CLIP_X11_PNG_LIBRARY)
 | 
			
		||||
        set(PNG_LIBRARY ${CLIP_X11_PNG_LIBRARY})
 | 
			
		||||
      else()
 | 
			
		||||
        find_library(PNG_LIBRARY png)
 | 
			
		||||
      endif()
 | 
			
		||||
      if(HAVE_PNG_H AND PNG_LIBRARY)
 | 
			
		||||
        target_compile_definitions(clip PRIVATE -DHAVE_PNG_H)
 | 
			
		||||
      endif()
 | 
			
		||||
      target_link_libraries(clip ${PNG_LIBRARY})
 | 
			
		||||
    endif()
 | 
			
		||||
    target_sources(clip PRIVATE clip_x11.cpp)
 | 
			
		||||
  else()
 | 
			
		||||
    target_sources(clip PRIVATE clip_none.cpp)
 | 
			
		||||
  endif()
 | 
			
		||||
else()
 | 
			
		||||
  target_sources(clip PRIVATE clip_none.cpp)
 | 
			
		||||
endif()
 | 
			
		||||
@@ -1,5 +0,0 @@
 | 
			
		||||
By submitting a pull request, you represent that you have the right to
 | 
			
		||||
license your contribution to the Clip project owners and the community,
 | 
			
		||||
agree by submitting the patch that your contributions are licensed under
 | 
			
		||||
the [Clip license](https://raw.githubusercontent.com/dacap/clip/main/LICENSE.txt),
 | 
			
		||||
and agree to future changes to the licensing.
 | 
			
		||||
@@ -1,77 +0,0 @@
 | 
			
		||||
# Clip Library
 | 
			
		||||
*Copyright (c) 2015-2024 David Capello*
 | 
			
		||||
 | 
			
		||||
[](https://github.com/dacap/clip/actions?query=workflow%3Abuild)
 | 
			
		||||
[](LICENSE.txt)
 | 
			
		||||
 | 
			
		||||
Library to copy/retrieve content to/from the clipboard/pasteboard.
 | 
			
		||||
 | 
			
		||||
## Features
 | 
			
		||||
 | 
			
		||||
Available features on Windows, macOS, and Linux (X11):
 | 
			
		||||
 | 
			
		||||
* Copy/paste UTF-8 text.
 | 
			
		||||
* Copy/paste user-defined data.
 | 
			
		||||
* Copy/paste RGB/RGBA images. This library use non-premultiplied alpha RGB values.
 | 
			
		||||
 | 
			
		||||
## Example
 | 
			
		||||
 | 
			
		||||
```cpp
 | 
			
		||||
#include "clip.h"
 | 
			
		||||
#include <iostream>
 | 
			
		||||
 | 
			
		||||
int main() {
 | 
			
		||||
  clip::set_text("Hello World");
 | 
			
		||||
 | 
			
		||||
  std::string value;
 | 
			
		||||
  clip::get_text(value);
 | 
			
		||||
  std::cout << value << "\n";
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## User-defined clipboard formats
 | 
			
		||||
 | 
			
		||||
```cpp
 | 
			
		||||
#include "clip.h"
 | 
			
		||||
 | 
			
		||||
int main() {
 | 
			
		||||
  clip::format my_format =
 | 
			
		||||
    clip::register_format("com.appname.FormatName");
 | 
			
		||||
 | 
			
		||||
  int value = 32;
 | 
			
		||||
 | 
			
		||||
  clip::lock l;
 | 
			
		||||
  l.clear();
 | 
			
		||||
  l.set_data(clip::text_format(), "Alternative text for value 32");
 | 
			
		||||
  l.set_data(my_format, &value, sizeof(int));
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Platform specific details
 | 
			
		||||
 | 
			
		||||
* If two versions of your application (32-bit and 64-bit) can run at
 | 
			
		||||
  at the same time, remember to avoid storing data types that could
 | 
			
		||||
  change depending on the platform (e.g. `size_t`) in your custom
 | 
			
		||||
  format data.
 | 
			
		||||
* **Windows**:
 | 
			
		||||
  - [Limited number of clipboard formats on Windows](http://blogs.msdn.com/b/oldnewthing/archive/2015/03/19/10601208.aspx)
 | 
			
		||||
* **Linux**:
 | 
			
		||||
  - To be able to copy/paste on Linux you need `libx11-dev`/`libX11-devel` package.
 | 
			
		||||
  - To copy/paste images you will need `libpng-dev`/`libpng-devel` package.
 | 
			
		||||
 | 
			
		||||
## Compilation Flags
 | 
			
		||||
 | 
			
		||||
* `CLIP_ENABLE_IMAGE`: Enables the support to
 | 
			
		||||
  [copy](examples/put_image.cpp)/[paste](examples/show_image.cpp) images.
 | 
			
		||||
* `CLIP_ENABLE_LIST_FORMATS` (only for Windows): Enables the
 | 
			
		||||
  `clip::lock::list_formats()` API function and the
 | 
			
		||||
  [list_clip_formats](examples/list_clip_formats.cpp) example.
 | 
			
		||||
* `CLIP_EXAMPLES`: Compile [examples](examples/).
 | 
			
		||||
* `CLIP_TESTS`: Compile [tests](tests/).
 | 
			
		||||
* `CLIP_X11_WITH_PNG` (only for Linux/X11): Enables support to
 | 
			
		||||
  copy/paste images using the `libpng` library on Linux.
 | 
			
		||||
 | 
			
		||||
## Who is using this library?
 | 
			
		||||
 | 
			
		||||
[Check the wiki](https://github.com/dacap/clip/wiki#who-is-using-clip)
 | 
			
		||||
to know what projects are using the `clip` library.
 | 
			
		||||
@@ -1,192 +0,0 @@
 | 
			
		||||
// Clip Library
 | 
			
		||||
// Copyright (c) 2015-2024 David Capello
 | 
			
		||||
//
 | 
			
		||||
// This file is released under the terms of the MIT license.
 | 
			
		||||
// Read LICENSE.txt for more information.
 | 
			
		||||
 | 
			
		||||
#include "clip.h"
 | 
			
		||||
#include "clip_lock_impl.h"
 | 
			
		||||
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <stdexcept>
 | 
			
		||||
 | 
			
		||||
namespace clip {
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
 | 
			
		||||
void default_error_handler(ErrorCode code) {
 | 
			
		||||
  static const char* err[] = {
 | 
			
		||||
    "Cannot lock clipboard",
 | 
			
		||||
    "Image format is not supported"
 | 
			
		||||
  };
 | 
			
		||||
  throw std::runtime_error(err[static_cast<int>(code)]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // anonymous namespace
 | 
			
		||||
 | 
			
		||||
error_handler g_error_handler = default_error_handler;
 | 
			
		||||
 | 
			
		||||
lock::lock(void* native_window_handle)
 | 
			
		||||
  : p(new impl(native_window_handle)) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
lock::~lock() = default;
 | 
			
		||||
 | 
			
		||||
bool lock::locked() const {
 | 
			
		||||
  return p->locked();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool lock::clear() {
 | 
			
		||||
  return p->clear();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool lock::is_convertible(format f) const {
 | 
			
		||||
  return p->is_convertible(f);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool lock::set_data(format f, const char* buf, size_t length) {
 | 
			
		||||
  return p->set_data(f, buf, length);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool lock::get_data(format f, char* buf, size_t len) const {
 | 
			
		||||
  return p->get_data(f, buf, len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t lock::get_data_length(format f) const {
 | 
			
		||||
  return p->get_data_length(f);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if CLIP_ENABLE_IMAGE
 | 
			
		||||
 | 
			
		||||
bool lock::set_image(const image& img) {
 | 
			
		||||
  return p->set_image(img);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool lock::get_image(image& img) const {
 | 
			
		||||
  return p->get_image(img);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool lock::get_image_spec(image_spec& spec) const {
 | 
			
		||||
  return p->get_image_spec(spec);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif // CLIP_ENABLE_IMAGE
 | 
			
		||||
 | 
			
		||||
#if CLIP_ENABLE_LIST_FORMATS
 | 
			
		||||
 | 
			
		||||
std::vector<format_info> lock::list_formats() const {
 | 
			
		||||
  return p->list_formats();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif // CLIP_ENABLE_LIST_FORMATS
 | 
			
		||||
 | 
			
		||||
format empty_format() { return 0; }
 | 
			
		||||
format text_format()  { return 1; }
 | 
			
		||||
#if CLIP_ENABLE_IMAGE
 | 
			
		||||
format image_format() { return 2; }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
bool has(format f) {
 | 
			
		||||
  lock l;
 | 
			
		||||
  if (l.locked())
 | 
			
		||||
    return l.is_convertible(f);
 | 
			
		||||
  else
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool clear() {
 | 
			
		||||
  lock l;
 | 
			
		||||
  if (l.locked())
 | 
			
		||||
    return l.clear();
 | 
			
		||||
  else
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool set_text(const std::string& value) {
 | 
			
		||||
  lock l;
 | 
			
		||||
  if (l.locked()) {
 | 
			
		||||
    l.clear();
 | 
			
		||||
    return l.set_data(text_format(), value.c_str(), value.size());
 | 
			
		||||
  }
 | 
			
		||||
  else
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool get_text(std::string& value) {
 | 
			
		||||
  lock l;
 | 
			
		||||
  if (!l.locked())
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  format f = text_format();
 | 
			
		||||
  if (!l.is_convertible(f))
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  size_t len = l.get_data_length(f);
 | 
			
		||||
  if (len > 0) {
 | 
			
		||||
    std::vector<char> buf(len);
 | 
			
		||||
    l.get_data(f, &buf[0], len);
 | 
			
		||||
    value = &buf[0];
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
  else {
 | 
			
		||||
    value.clear();
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if CLIP_ENABLE_IMAGE
 | 
			
		||||
 | 
			
		||||
bool set_image(const image& img) {
 | 
			
		||||
  lock l;
 | 
			
		||||
  if (l.locked()) {
 | 
			
		||||
    l.clear();
 | 
			
		||||
    return l.set_image(img);
 | 
			
		||||
  }
 | 
			
		||||
  else
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool get_image(image& img) {
 | 
			
		||||
  lock l;
 | 
			
		||||
  if (!l.locked())
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  format f = image_format();
 | 
			
		||||
  if (!l.is_convertible(f))
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  return l.get_image(img);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool get_image_spec(image_spec& spec) {
 | 
			
		||||
  lock l;
 | 
			
		||||
  if (!l.locked())
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  format f = image_format();
 | 
			
		||||
  if (!l.is_convertible(f))
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  return l.get_image_spec(spec);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif // CLIP_ENABLE_IMAGE
 | 
			
		||||
 | 
			
		||||
void set_error_handler(error_handler handler) {
 | 
			
		||||
  g_error_handler = handler;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
error_handler get_error_handler() {
 | 
			
		||||
  return g_error_handler;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_XCB_XLIB_H
 | 
			
		||||
static int g_x11_timeout = 1000;
 | 
			
		||||
void set_x11_wait_timeout(int msecs) { g_x11_timeout = msecs; }
 | 
			
		||||
int get_x11_wait_timeout() { return g_x11_timeout; }
 | 
			
		||||
#else
 | 
			
		||||
void set_x11_wait_timeout(int) { }
 | 
			
		||||
int get_x11_wait_timeout() { return 1000; }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
} // namespace clip
 | 
			
		||||
@@ -1,211 +0,0 @@
 | 
			
		||||
// Clip Library
 | 
			
		||||
// Copyright (c) 2015-2024 David Capello
 | 
			
		||||
//
 | 
			
		||||
// This file is released under the terms of the MIT license.
 | 
			
		||||
// Read LICENSE.txt for more information.
 | 
			
		||||
 | 
			
		||||
#ifndef CLIP_H_INCLUDED
 | 
			
		||||
#define CLIP_H_INCLUDED
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <cassert>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
namespace clip {
 | 
			
		||||
 | 
			
		||||
  // ======================================================================
 | 
			
		||||
  // Low-level API to lock the clipboard/pasteboard and modify it
 | 
			
		||||
  // ======================================================================
 | 
			
		||||
 | 
			
		||||
  // Clipboard format identifier.
 | 
			
		||||
  typedef size_t format;
 | 
			
		||||
 | 
			
		||||
#if CLIP_ENABLE_IMAGE
 | 
			
		||||
  class image;
 | 
			
		||||
  struct image_spec;
 | 
			
		||||
#endif // CLIP_ENABLE_IMAGE
 | 
			
		||||
 | 
			
		||||
#if CLIP_ENABLE_LIST_FORMATS
 | 
			
		||||
  struct format_info {
 | 
			
		||||
    format id = 0;
 | 
			
		||||
    std::string name;
 | 
			
		||||
    format_info(const format id,
 | 
			
		||||
                const std::string& name)
 | 
			
		||||
      : id(id),
 | 
			
		||||
        name(name) {
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
#endif // CLIP_ENABLE_LIST_FORMATS
 | 
			
		||||
 | 
			
		||||
  class lock {
 | 
			
		||||
  public:
 | 
			
		||||
    // You can give your current HWND as the "native_window_handle."
 | 
			
		||||
    // Windows clipboard functions use this handle to open/close
 | 
			
		||||
    // (lock/unlock) the clipboard. From the MSDN documentation we
 | 
			
		||||
    // need this handler so SetClipboardData() doesn't fail after a
 | 
			
		||||
    // EmptyClipboard() call. Anyway it looks to work just fine if we
 | 
			
		||||
    // call OpenClipboard() with a null HWND.
 | 
			
		||||
    lock(void* native_window_handle = nullptr);
 | 
			
		||||
    ~lock();
 | 
			
		||||
 | 
			
		||||
    // Returns true if we've locked the clipboard successfully in
 | 
			
		||||
    // lock() constructor.
 | 
			
		||||
    bool locked() const;
 | 
			
		||||
 | 
			
		||||
    // Clears the clipboard content. If you don't clear the content,
 | 
			
		||||
    // previous clipboard content (in unknown formats) could persist
 | 
			
		||||
    // after the unlock.
 | 
			
		||||
    bool clear();
 | 
			
		||||
 | 
			
		||||
    // Returns true if the clipboard can be converted to the given
 | 
			
		||||
    // format.
 | 
			
		||||
    bool is_convertible(format f) const;
 | 
			
		||||
    bool set_data(format f, const char* buf, size_t len);
 | 
			
		||||
    bool get_data(format f, char* buf, size_t len) const;
 | 
			
		||||
    size_t get_data_length(format f) const;
 | 
			
		||||
 | 
			
		||||
#if CLIP_ENABLE_IMAGE
 | 
			
		||||
    // For images
 | 
			
		||||
    bool set_image(const image& image);
 | 
			
		||||
    bool get_image(image& image) const;
 | 
			
		||||
    bool get_image_spec(image_spec& spec) const;
 | 
			
		||||
#endif // CLIP_ENABLE_IMAGE
 | 
			
		||||
 | 
			
		||||
#if CLIP_ENABLE_LIST_FORMATS
 | 
			
		||||
    // Returns the list of available formats (by name) in the
 | 
			
		||||
    // clipboard.
 | 
			
		||||
    std::vector<format_info> list_formats() const;
 | 
			
		||||
#endif // CLIP_ENABLE_LIST_FORMATS
 | 
			
		||||
 | 
			
		||||
  private:
 | 
			
		||||
    class impl;
 | 
			
		||||
    std::unique_ptr<impl> p;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  format register_format(const std::string& name);
 | 
			
		||||
 | 
			
		||||
  // This format is when the clipboard has no content.
 | 
			
		||||
  format empty_format();
 | 
			
		||||
 | 
			
		||||
  // When the clipboard has UTF8 text.
 | 
			
		||||
  format text_format();
 | 
			
		||||
 | 
			
		||||
#if CLIP_ENABLE_IMAGE
 | 
			
		||||
  // When the clipboard has an image.
 | 
			
		||||
  format image_format();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  // Returns true if the clipboard has content of the given type.
 | 
			
		||||
  bool has(format f);
 | 
			
		||||
 | 
			
		||||
  // Clears the clipboard content.
 | 
			
		||||
  bool clear();
 | 
			
		||||
 | 
			
		||||
  // ======================================================================
 | 
			
		||||
  // Error handling
 | 
			
		||||
  // ======================================================================
 | 
			
		||||
 | 
			
		||||
  enum class ErrorCode {
 | 
			
		||||
    CannotLock,
 | 
			
		||||
#if CLIP_ENABLE_IMAGE
 | 
			
		||||
    ImageNotSupported,
 | 
			
		||||
#endif
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  typedef void (*error_handler)(ErrorCode code);
 | 
			
		||||
 | 
			
		||||
  void set_error_handler(error_handler f);
 | 
			
		||||
  error_handler get_error_handler();
 | 
			
		||||
 | 
			
		||||
  // ======================================================================
 | 
			
		||||
  // Text
 | 
			
		||||
  // ======================================================================
 | 
			
		||||
 | 
			
		||||
  // High-level API to put/get UTF8 text in/from the clipboard. These
 | 
			
		||||
  // functions returns false in case of error.
 | 
			
		||||
  bool set_text(const std::string& value);
 | 
			
		||||
  bool get_text(std::string& value);
 | 
			
		||||
 | 
			
		||||
  // ======================================================================
 | 
			
		||||
  // Image
 | 
			
		||||
  // ======================================================================
 | 
			
		||||
 | 
			
		||||
#if CLIP_ENABLE_IMAGE
 | 
			
		||||
 | 
			
		||||
  struct image_spec {
 | 
			
		||||
    unsigned long width = 0;
 | 
			
		||||
    unsigned long height = 0;
 | 
			
		||||
    unsigned long bits_per_pixel = 0;
 | 
			
		||||
    unsigned long bytes_per_row = 0;
 | 
			
		||||
    unsigned long red_mask = 0;
 | 
			
		||||
    unsigned long green_mask = 0;
 | 
			
		||||
    unsigned long blue_mask = 0;
 | 
			
		||||
    unsigned long alpha_mask = 0;
 | 
			
		||||
    unsigned long red_shift = 0;
 | 
			
		||||
    unsigned long green_shift = 0;
 | 
			
		||||
    unsigned long blue_shift = 0;
 | 
			
		||||
    unsigned long alpha_shift = 0;
 | 
			
		||||
 | 
			
		||||
    unsigned long required_data_size() const;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // The image data must contain straight RGB values
 | 
			
		||||
  // (non-premultiplied by alpha). The image retrieved from the
 | 
			
		||||
  // clipboard will be non-premultiplied too. Basically you will be
 | 
			
		||||
  // always dealing with straight alpha images.
 | 
			
		||||
  //
 | 
			
		||||
  // Details: Windows expects premultiplied images on its clipboard
 | 
			
		||||
  // content, so the library code make the proper conversion
 | 
			
		||||
  // automatically. macOS handles straight alpha directly, so there is
 | 
			
		||||
  // no conversion at all. Linux/X11 images are transferred in
 | 
			
		||||
  // image/png format which are specified in straight alpha.
 | 
			
		||||
  class image {
 | 
			
		||||
  public:
 | 
			
		||||
    image();
 | 
			
		||||
    image(const image_spec& spec);
 | 
			
		||||
    image(const void* data, const image_spec& spec);
 | 
			
		||||
    image(const image& image);
 | 
			
		||||
    image(image&& image);
 | 
			
		||||
    ~image();
 | 
			
		||||
 | 
			
		||||
    image& operator=(const image& image);
 | 
			
		||||
    image& operator=(image&& image);
 | 
			
		||||
 | 
			
		||||
    char* data() const { return m_data; }
 | 
			
		||||
    const image_spec& spec() const { return m_spec; }
 | 
			
		||||
 | 
			
		||||
    bool is_valid() const { return m_data != nullptr; }
 | 
			
		||||
    void reset();
 | 
			
		||||
 | 
			
		||||
  private:
 | 
			
		||||
    void copy_image(const image& image);
 | 
			
		||||
    void move_image(image&& image);
 | 
			
		||||
 | 
			
		||||
    bool m_own_data;
 | 
			
		||||
    char* m_data;
 | 
			
		||||
    image_spec m_spec;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // High-level API to set/get an image in/from the clipboard. These
 | 
			
		||||
  // functions returns false in case of error.
 | 
			
		||||
  bool set_image(const image& img);
 | 
			
		||||
  bool get_image(image& img);
 | 
			
		||||
  bool get_image_spec(image_spec& spec);
 | 
			
		||||
 | 
			
		||||
#endif // CLIP_ENABLE_IMAGE
 | 
			
		||||
 | 
			
		||||
  // ======================================================================
 | 
			
		||||
  // Platform-specific
 | 
			
		||||
  // ======================================================================
 | 
			
		||||
 | 
			
		||||
  // Only for X11: Sets the time (in milliseconds) that we must wait
 | 
			
		||||
  // for the selection/clipboard owner to receive the content. This
 | 
			
		||||
  // value is 1000 (one second) by default.
 | 
			
		||||
  void set_x11_wait_timeout(int msecs);
 | 
			
		||||
  int get_x11_wait_timeout();
 | 
			
		||||
 | 
			
		||||
} // namespace clip
 | 
			
		||||
 | 
			
		||||
#endif // CLIP_H_INCLUDED
 | 
			
		||||
@@ -1,82 +0,0 @@
 | 
			
		||||
// Clip Library
 | 
			
		||||
// Copyright (C) 2020-2024 David Capello
 | 
			
		||||
//
 | 
			
		||||
// This file is released under the terms of the MIT license.
 | 
			
		||||
// Read LICENSE.txt for more information.
 | 
			
		||||
 | 
			
		||||
#ifndef CLIP_COMMON_H_INCLUDED
 | 
			
		||||
#define CLIP_COMMON_H_INCLUDED
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "clip.h"
 | 
			
		||||
 | 
			
		||||
namespace clip {
 | 
			
		||||
namespace details {
 | 
			
		||||
 | 
			
		||||
#if CLIP_ENABLE_IMAGE
 | 
			
		||||
 | 
			
		||||
inline void divide_rgb_by_alpha(image& img,
 | 
			
		||||
                                bool hasAlphaGreaterThanZero = false) {
 | 
			
		||||
  const image_spec& spec = img.spec();
 | 
			
		||||
 | 
			
		||||
  bool hasValidPremultipliedAlpha = true;
 | 
			
		||||
 | 
			
		||||
  for (unsigned long y=0; y<spec.height; ++y) {
 | 
			
		||||
    const uint32_t* dst = (uint32_t*)(img.data()+y*spec.bytes_per_row);
 | 
			
		||||
    for (unsigned long x=0; x<spec.width; ++x, ++dst) {
 | 
			
		||||
      const uint32_t c = *dst;
 | 
			
		||||
      const int r = ((c & spec.red_mask  ) >> spec.red_shift  );
 | 
			
		||||
      const int g = ((c & spec.green_mask) >> spec.green_shift);
 | 
			
		||||
      const int b = ((c & spec.blue_mask ) >> spec.blue_shift );
 | 
			
		||||
      const int a = ((c & spec.alpha_mask) >> spec.alpha_shift);
 | 
			
		||||
 | 
			
		||||
      if (a > 0)
 | 
			
		||||
        hasAlphaGreaterThanZero = true;
 | 
			
		||||
      if (r > a || g > a || b > a)
 | 
			
		||||
        hasValidPremultipliedAlpha = false;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  for (unsigned long y=0; y<spec.height; ++y) {
 | 
			
		||||
    uint32_t* dst = (uint32_t*)(img.data()+y*spec.bytes_per_row);
 | 
			
		||||
    for (unsigned long x=0; x<spec.width; ++x, ++dst) {
 | 
			
		||||
      const uint32_t c = *dst;
 | 
			
		||||
      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);
 | 
			
		||||
 | 
			
		||||
      // If all alpha values = 0, we make the image opaque.
 | 
			
		||||
      if (!hasAlphaGreaterThanZero) {
 | 
			
		||||
        a = 255;
 | 
			
		||||
 | 
			
		||||
        // We cannot change the image spec (e.g. spec.alpha_mask=0) to
 | 
			
		||||
        // make the image opaque, because the "spec" of the image is
 | 
			
		||||
        // read-only. The image spec used by the client is the one
 | 
			
		||||
        // returned by get_image_spec().
 | 
			
		||||
      }
 | 
			
		||||
      // If there is alpha information and it's pre-multiplied alpha
 | 
			
		||||
      else if (hasValidPremultipliedAlpha) {
 | 
			
		||||
        if (a > 0) {
 | 
			
		||||
          // Convert it to straight alpha
 | 
			
		||||
          r = r * 255 / a;
 | 
			
		||||
          g = g * 255 / a;
 | 
			
		||||
          b = b * 255 / a;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      *dst =
 | 
			
		||||
        (r << spec.red_shift  ) |
 | 
			
		||||
        (g << spec.green_shift) |
 | 
			
		||||
        (b << spec.blue_shift ) |
 | 
			
		||||
        (a << spec.alpha_shift);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif // CLIP_ENABLE_IMAGE
 | 
			
		||||
 | 
			
		||||
} // namespace details
 | 
			
		||||
} // namespace clip
 | 
			
		||||
 | 
			
		||||
#endif // CLIP_H_INCLUDED
 | 
			
		||||
@@ -1,40 +0,0 @@
 | 
			
		||||
// Clip Library
 | 
			
		||||
// Copyright (c) 2015-2024 David Capello
 | 
			
		||||
//
 | 
			
		||||
// This file is released under the terms of the MIT license.
 | 
			
		||||
// Read LICENSE.txt for more information.
 | 
			
		||||
 | 
			
		||||
#ifndef CLIP_LOCK_IMPL_H_INCLUDED
 | 
			
		||||
#define CLIP_LOCK_IMPL_H_INCLUDED
 | 
			
		||||
 | 
			
		||||
namespace clip {
 | 
			
		||||
 | 
			
		||||
class lock::impl {
 | 
			
		||||
public:
 | 
			
		||||
  impl(void* native_window_handle);
 | 
			
		||||
  ~impl();
 | 
			
		||||
 | 
			
		||||
  bool locked() const { return m_locked; }
 | 
			
		||||
  bool clear();
 | 
			
		||||
  bool is_convertible(format f) const;
 | 
			
		||||
  bool set_data(format f, const char* buf, size_t len);
 | 
			
		||||
  bool get_data(format f, char* buf, size_t len) const;
 | 
			
		||||
  size_t get_data_length(format f) const;
 | 
			
		||||
 | 
			
		||||
#if CLIP_ENABLE_IMAGE
 | 
			
		||||
  bool set_image(const image& image);
 | 
			
		||||
  bool get_image(image& image) const;
 | 
			
		||||
  bool get_image_spec(image_spec& spec) const;
 | 
			
		||||
#endif // CLIP_ENABLE_IMAGE
 | 
			
		||||
 | 
			
		||||
#if CLIP_ENABLE_LIST_FORMATS
 | 
			
		||||
  std::vector<format_info> list_formats() const;
 | 
			
		||||
#endif // CLIP_ENABLE_LIST_FORMATS
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
  bool m_locked;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace clip
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
@@ -1,86 +0,0 @@
 | 
			
		||||
// Clip Library
 | 
			
		||||
// Copyright (c) 2015-2018 David Capello
 | 
			
		||||
//
 | 
			
		||||
// This file is released under the terms of the MIT license.
 | 
			
		||||
// Read LICENSE.txt for more information.
 | 
			
		||||
 | 
			
		||||
#include "clip.h"
 | 
			
		||||
#include "clip_lock_impl.h"
 | 
			
		||||
 | 
			
		||||
#include <cassert>
 | 
			
		||||
#include <map>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
namespace clip {
 | 
			
		||||
 | 
			
		||||
typedef std::vector<char> Buffer;
 | 
			
		||||
typedef std::map<format, Buffer> Map;
 | 
			
		||||
 | 
			
		||||
static format g_last_format = 100; // TODO create an enum with common formats
 | 
			
		||||
static Map g_data;
 | 
			
		||||
 | 
			
		||||
lock::impl::impl(void* native_handle) : m_locked(true) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
lock::impl::~impl() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool lock::impl::clear() {
 | 
			
		||||
  g_data.clear();
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool lock::impl::is_convertible(format f) const {
 | 
			
		||||
  return (g_data.find(f) != g_data.end());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool lock::impl::set_data(format f, const char* buf, size_t len) {
 | 
			
		||||
  Buffer& dst = g_data[f];
 | 
			
		||||
 | 
			
		||||
  dst.resize(len);
 | 
			
		||||
  if (buf && len > 0)
 | 
			
		||||
    std::copy(buf, buf+len, dst.begin());
 | 
			
		||||
 | 
			
		||||
  if (f == text_format() &&
 | 
			
		||||
      len > 0 && dst.back() != 0) {
 | 
			
		||||
    dst.push_back(0);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool lock::impl::get_data(format f, char* buf, size_t len) const {
 | 
			
		||||
  assert(buf);
 | 
			
		||||
 | 
			
		||||
  if (!buf || !is_convertible(f))
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  const Buffer& src = g_data[f];
 | 
			
		||||
  std::copy(src.begin(), src.end(), buf);
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t lock::impl::get_data_length(format f) const {
 | 
			
		||||
  if (is_convertible(f))
 | 
			
		||||
    return g_data[f].size();
 | 
			
		||||
  else
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool lock::impl::set_image(const image& image) {
 | 
			
		||||
  return false;               // TODO
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool lock::impl::get_image(image& image) const {
 | 
			
		||||
  return false;               // TODO
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool lock::impl::get_image_spec(image_spec& spec) const {
 | 
			
		||||
  return false;               // TODO
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
format register_format(const std::string& name) {
 | 
			
		||||
  return g_last_format++;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace clip
 | 
			
		||||
@@ -1,35 +0,0 @@
 | 
			
		||||
// Clip Library
 | 
			
		||||
// Copyright (c) 2024 David Capello
 | 
			
		||||
//
 | 
			
		||||
// This file is released under the terms of the MIT license.
 | 
			
		||||
// Read LICENSE.txt for more information.
 | 
			
		||||
 | 
			
		||||
#ifndef CLIP_OSX_H_INCLUDED
 | 
			
		||||
#define CLIP_OSX_H_INCLUDED
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#ifdef __OBJC__
 | 
			
		||||
 | 
			
		||||
#include <Cocoa/Cocoa.h>
 | 
			
		||||
 | 
			
		||||
namespace clip {
 | 
			
		||||
 | 
			
		||||
class image;
 | 
			
		||||
struct image_spec;
 | 
			
		||||
 | 
			
		||||
namespace osx {
 | 
			
		||||
 | 
			
		||||
#if CLIP_ENABLE_IMAGE
 | 
			
		||||
 | 
			
		||||
bool get_image_from_clipboard(NSPasteboard* pasteboard,
 | 
			
		||||
                              image* output_img,
 | 
			
		||||
                              image_spec* output_spec);
 | 
			
		||||
 | 
			
		||||
#endif // CLIP_ENABLE_IMAGE
 | 
			
		||||
 | 
			
		||||
} // namespace osx
 | 
			
		||||
} // namespace clip
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
@@ -1,377 +0,0 @@
 | 
			
		||||
// Clip Library
 | 
			
		||||
// Copyright (c) 2015-2023 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 <cassert>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <map>
 | 
			
		||||
 | 
			
		||||
#include <CoreFoundation/CoreFoundation.h>
 | 
			
		||||
#include <Foundation/Foundation.h>
 | 
			
		||||
#include <AppKit/AppKit.h>
 | 
			
		||||
 | 
			
		||||
namespace clip {
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
 | 
			
		||||
  format g_last_format = 100;
 | 
			
		||||
  std::map<std::string, format> g_name_to_format;
 | 
			
		||||
  std::map<format, std::string> g_format_to_name;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
namespace osx {
 | 
			
		||||
 | 
			
		||||
#if CLIP_ENABLE_IMAGE
 | 
			
		||||
 | 
			
		||||
  bool get_image_from_clipboard(NSPasteboard* pasteboard,
 | 
			
		||||
                                image* output_img,
 | 
			
		||||
                                image_spec* output_spec)
 | 
			
		||||
  {
 | 
			
		||||
    NSString* result = [pasteboard availableTypeFromArray:
 | 
			
		||||
                                [NSArray arrayWithObjects:NSPasteboardTypeTIFF,NSPasteboardTypePNG,nil]];
 | 
			
		||||
 | 
			
		||||
    if (!result)
 | 
			
		||||
      return false;
 | 
			
		||||
 | 
			
		||||
    NSData* data = [pasteboard dataForType:result];
 | 
			
		||||
    if (!data)
 | 
			
		||||
      return false;
 | 
			
		||||
 | 
			
		||||
    NSBitmapImageRep* bitmap = [NSBitmapImageRep imageRepWithData:data];
 | 
			
		||||
 | 
			
		||||
    if ((bitmap.bitmapFormat & NSBitmapFormatFloatingPointSamples) ||
 | 
			
		||||
        (bitmap.planar)) {
 | 
			
		||||
      error_handler e = get_error_handler();
 | 
			
		||||
      if (e)
 | 
			
		||||
        e(ErrorCode::ImageNotSupported);
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    image_spec spec;
 | 
			
		||||
    spec.width = bitmap.pixelsWide;
 | 
			
		||||
    spec.height = bitmap.pixelsHigh;
 | 
			
		||||
    spec.bits_per_pixel = bitmap.bitsPerPixel;
 | 
			
		||||
    spec.bytes_per_row = bitmap.bytesPerRow;
 | 
			
		||||
 | 
			
		||||
    // We need three samples for Red/Green/Blue
 | 
			
		||||
    if (bitmap.samplesPerPixel >= 3) {
 | 
			
		||||
      // Here we are guessing the bits per sample (generally 8, not
 | 
			
		||||
      // sure how many bits per sample macOS uses for 16bpp
 | 
			
		||||
      // NSBitmapFormat or if this format is even used).
 | 
			
		||||
      int bits_per_sample = (bitmap.bitsPerPixel == 16 ? 5: 8);
 | 
			
		||||
      int bits_shift = 0;
 | 
			
		||||
 | 
			
		||||
      // With alpha
 | 
			
		||||
      if (bitmap.alpha) {
 | 
			
		||||
        if (bitmap.bitmapFormat & NSBitmapFormatAlphaFirst) {
 | 
			
		||||
          spec.alpha_shift = 0;
 | 
			
		||||
          bits_shift += bits_per_sample;
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
          spec.alpha_shift = 3*bits_per_sample;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      unsigned long* masks = &spec.red_mask;
 | 
			
		||||
      unsigned long* shifts = &spec.red_shift;
 | 
			
		||||
 | 
			
		||||
      // Red/green/blue shifts
 | 
			
		||||
      for (unsigned long* shift=shifts; shift<shifts+3; ++shift) {
 | 
			
		||||
        *shift = bits_shift;
 | 
			
		||||
        bits_shift += bits_per_sample;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // With alpha
 | 
			
		||||
      if (bitmap.alpha) {
 | 
			
		||||
        if (bitmap.bitmapFormat & NSBitmapFormatSixteenBitBigEndian ||
 | 
			
		||||
            bitmap.bitmapFormat & NSBitmapFormatThirtyTwoBitBigEndian) {
 | 
			
		||||
          std::swap(spec.red_shift, spec.alpha_shift);
 | 
			
		||||
          std::swap(spec.green_shift, spec.blue_shift);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      // Without alpha
 | 
			
		||||
      else {
 | 
			
		||||
        if (bitmap.bitmapFormat & NSBitmapFormatSixteenBitBigEndian ||
 | 
			
		||||
            bitmap.bitmapFormat & NSBitmapFormatThirtyTwoBitBigEndian) {
 | 
			
		||||
          std::swap(spec.red_shift, spec.blue_shift);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // Calculate all masks
 | 
			
		||||
      for (unsigned long* shift=shifts, *mask=masks; shift<shifts+4; ++shift, ++mask)
 | 
			
		||||
        *mask = ((1ul<<bits_per_sample)-1ul) << (*shift);
 | 
			
		||||
 | 
			
		||||
      // Without alpha
 | 
			
		||||
      if (!bitmap.alpha)
 | 
			
		||||
        spec.alpha_mask = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (output_spec) {
 | 
			
		||||
      *output_spec = spec;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (output_img) {
 | 
			
		||||
      unsigned long size = spec.bytes_per_row*spec.height;
 | 
			
		||||
      image img(spec);
 | 
			
		||||
 | 
			
		||||
      std::copy(bitmap.bitmapData,
 | 
			
		||||
                bitmap.bitmapData+size, img.data());
 | 
			
		||||
 | 
			
		||||
      // Convert premultiplied data to unpremultiplied if needed.
 | 
			
		||||
      if (bitmap.alpha &&
 | 
			
		||||
          bitmap.samplesPerPixel >= 3 &&
 | 
			
		||||
          !(bitmap.bitmapFormat & NSBitmapFormatAlphaNonpremultiplied)) {
 | 
			
		||||
        details::divide_rgb_by_alpha(
 | 
			
		||||
          img,
 | 
			
		||||
          true); // hasAlphaGreaterThanZero=true because we have valid alpha information
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      std::swap(*output_img, img);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
#endif // CLIP_ENABLE_IMAGE
 | 
			
		||||
 | 
			
		||||
} // namespace osx
 | 
			
		||||
 | 
			
		||||
lock::impl::impl(void*) : m_locked(true) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
lock::impl::~impl() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool lock::impl::clear() {
 | 
			
		||||
  @autoreleasepool {
 | 
			
		||||
    NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
 | 
			
		||||
    [pasteboard clearContents];
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool lock::impl::is_convertible(format f) const {
 | 
			
		||||
  @autoreleasepool {
 | 
			
		||||
    NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
 | 
			
		||||
    NSString* result = nil;
 | 
			
		||||
 | 
			
		||||
    if (f == text_format()) {
 | 
			
		||||
      result = [pasteboard availableTypeFromArray:[NSArray arrayWithObject:NSPasteboardTypeString]];
 | 
			
		||||
    }
 | 
			
		||||
#if CLIP_ENABLE_IMAGE
 | 
			
		||||
    else if (f == image_format()) {
 | 
			
		||||
      result = [pasteboard availableTypeFromArray:
 | 
			
		||||
                        [NSArray arrayWithObjects:NSPasteboardTypeTIFF,NSPasteboardTypePNG,nil]];
 | 
			
		||||
    }
 | 
			
		||||
#endif // CLIP_ENABLE_IMAGE
 | 
			
		||||
    else {
 | 
			
		||||
      auto it = g_format_to_name.find(f);
 | 
			
		||||
      if (it != g_format_to_name.end()) {
 | 
			
		||||
        const std::string& name = it->second;
 | 
			
		||||
        NSString* string = [[NSString alloc] initWithBytesNoCopy:(void*)name.c_str()
 | 
			
		||||
                                                          length:name.size()
 | 
			
		||||
                                                        encoding:NSUTF8StringEncoding
 | 
			
		||||
                                                    freeWhenDone:NO];
 | 
			
		||||
        result = [pasteboard availableTypeFromArray:[NSArray arrayWithObject:string]];
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return (result ? true: false);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool lock::impl::set_data(format f, const char* buf, size_t len) {
 | 
			
		||||
  @autoreleasepool {
 | 
			
		||||
    NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
 | 
			
		||||
 | 
			
		||||
    if (f == text_format()) {
 | 
			
		||||
      NSString* string = [[NSString alloc] initWithBytesNoCopy:(void*)buf
 | 
			
		||||
                                                        length:len
 | 
			
		||||
                                                      encoding:NSUTF8StringEncoding
 | 
			
		||||
                                                  freeWhenDone:NO];
 | 
			
		||||
      [pasteboard setString:string forType:NSPasteboardTypeString];
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      auto it = g_format_to_name.find(f);
 | 
			
		||||
      if (it != g_format_to_name.end()) {
 | 
			
		||||
        const std::string& formatName = it->second;
 | 
			
		||||
        NSString* typeString = [[NSString alloc]
 | 
			
		||||
                                 initWithBytesNoCopy:(void*)formatName.c_str()
 | 
			
		||||
                                              length:formatName.size()
 | 
			
		||||
                                            encoding:NSUTF8StringEncoding
 | 
			
		||||
                                        freeWhenDone:NO];
 | 
			
		||||
        NSData* data = [NSData dataWithBytesNoCopy:(void*)buf
 | 
			
		||||
                                            length:len
 | 
			
		||||
                                      freeWhenDone:NO];
 | 
			
		||||
 | 
			
		||||
        if ([pasteboard setData:data forType:typeString])
 | 
			
		||||
          return true;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool lock::impl::get_data(format f, char* buf, size_t len) const {
 | 
			
		||||
  @autoreleasepool {
 | 
			
		||||
    assert(buf);
 | 
			
		||||
    if (!buf || !is_convertible(f))
 | 
			
		||||
      return false;
 | 
			
		||||
 | 
			
		||||
    NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
 | 
			
		||||
 | 
			
		||||
    if (f == text_format()) {
 | 
			
		||||
      NSString* string = [pasteboard stringForType:NSPasteboardTypeString];
 | 
			
		||||
      int reqsize = [string lengthOfBytesUsingEncoding:NSUTF8StringEncoding]+1;
 | 
			
		||||
 | 
			
		||||
      assert(reqsize <= len);
 | 
			
		||||
      if (reqsize > len) {
 | 
			
		||||
        // Buffer is too small
 | 
			
		||||
        return false;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (reqsize == 0)
 | 
			
		||||
        return true;
 | 
			
		||||
 | 
			
		||||
      memcpy(buf, [string UTF8String], reqsize);
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto it = g_format_to_name.find(f);
 | 
			
		||||
    if (it == g_format_to_name.end())
 | 
			
		||||
      return false;
 | 
			
		||||
 | 
			
		||||
    const std::string& formatName = it->second;
 | 
			
		||||
    NSString* typeString =
 | 
			
		||||
      [[NSString alloc] initWithBytesNoCopy:(void*)formatName.c_str()
 | 
			
		||||
                                     length:formatName.size()
 | 
			
		||||
                                   encoding:NSUTF8StringEncoding
 | 
			
		||||
                               freeWhenDone:NO];
 | 
			
		||||
 | 
			
		||||
    NSData* data = [pasteboard dataForType:typeString];
 | 
			
		||||
    if (!data)
 | 
			
		||||
      return false;
 | 
			
		||||
 | 
			
		||||
    [data getBytes:buf length:len];
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t lock::impl::get_data_length(format f) const {
 | 
			
		||||
  @autoreleasepool {
 | 
			
		||||
    NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
 | 
			
		||||
 | 
			
		||||
    if (f == text_format()) {
 | 
			
		||||
      NSString* string = [pasteboard stringForType:NSPasteboardTypeString];
 | 
			
		||||
      return [string lengthOfBytesUsingEncoding:NSUTF8StringEncoding]+1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto it = g_format_to_name.find(f);
 | 
			
		||||
    if (it == g_format_to_name.end())
 | 
			
		||||
      return 0;
 | 
			
		||||
 | 
			
		||||
    const std::string& formatName = it->second;
 | 
			
		||||
    NSString* typeString =
 | 
			
		||||
      [[NSString alloc] initWithBytesNoCopy:(void*)formatName.c_str()
 | 
			
		||||
                                     length:formatName.size()
 | 
			
		||||
                                   encoding:NSUTF8StringEncoding
 | 
			
		||||
                               freeWhenDone:NO];
 | 
			
		||||
 | 
			
		||||
    NSData* data = [pasteboard dataForType:typeString];
 | 
			
		||||
    if (!data)
 | 
			
		||||
      return 0;
 | 
			
		||||
 | 
			
		||||
    return data.length;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if CLIP_ENABLE_IMAGE
 | 
			
		||||
 | 
			
		||||
bool lock::impl::set_image(const image& image) {
 | 
			
		||||
  @autoreleasepool {
 | 
			
		||||
    NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
 | 
			
		||||
    const image_spec& spec = image.spec();
 | 
			
		||||
 | 
			
		||||
    NSBitmapFormat bitmapFormat = 0;
 | 
			
		||||
    int samples_per_pixel = 0;
 | 
			
		||||
    if (spec.alpha_mask) {
 | 
			
		||||
      samples_per_pixel = 4;
 | 
			
		||||
      if (spec.alpha_shift == 0)
 | 
			
		||||
        bitmapFormat |= NSBitmapFormatAlphaFirst;
 | 
			
		||||
      bitmapFormat |= NSBitmapFormatAlphaNonpremultiplied;
 | 
			
		||||
    }
 | 
			
		||||
    else if (spec.red_mask || spec.green_mask || spec.blue_mask) {
 | 
			
		||||
      samples_per_pixel = 3;
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      samples_per_pixel = 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (spec.bits_per_pixel == 32)
 | 
			
		||||
      bitmapFormat |= NSBitmapFormatThirtyTwoBitLittleEndian;
 | 
			
		||||
    else if (spec.bits_per_pixel == 16)
 | 
			
		||||
      bitmapFormat |= NSBitmapFormatSixteenBitLittleEndian;
 | 
			
		||||
 | 
			
		||||
    std::vector<unsigned char*> planes(1);
 | 
			
		||||
    planes[0] = (unsigned char*)image.data();
 | 
			
		||||
 | 
			
		||||
    NSBitmapImageRep* bitmap =
 | 
			
		||||
      [[NSBitmapImageRep alloc]
 | 
			
		||||
        initWithBitmapDataPlanes:&planes[0]
 | 
			
		||||
                      pixelsWide:spec.width
 | 
			
		||||
                      pixelsHigh:spec.height
 | 
			
		||||
                   bitsPerSample:spec.bits_per_pixel / samples_per_pixel
 | 
			
		||||
                 samplesPerPixel:samples_per_pixel
 | 
			
		||||
                        hasAlpha:(spec.alpha_mask ? YES: NO)
 | 
			
		||||
                        isPlanar:NO
 | 
			
		||||
                  colorSpaceName:NSDeviceRGBColorSpace
 | 
			
		||||
                    bitmapFormat:bitmapFormat
 | 
			
		||||
                     bytesPerRow:spec.bytes_per_row
 | 
			
		||||
                    bitsPerPixel:spec.bits_per_pixel];
 | 
			
		||||
    if (!bitmap)
 | 
			
		||||
      return false;
 | 
			
		||||
 | 
			
		||||
    NSData* data = bitmap.TIFFRepresentation;
 | 
			
		||||
    if (!data)
 | 
			
		||||
      return false;
 | 
			
		||||
 | 
			
		||||
    if ([pasteboard setData:data forType:NSPasteboardTypeTIFF])
 | 
			
		||||
      return true;
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool lock::impl::get_image(image& img) const {
 | 
			
		||||
  NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
 | 
			
		||||
  return osx::get_image_from_clipboard(pasteboard, &img, nullptr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool lock::impl::get_image_spec(image_spec& spec) const {
 | 
			
		||||
  NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
 | 
			
		||||
  return osx::get_image_from_clipboard(pasteboard, nullptr, &spec);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif // CLIP_ENABLE_IMAGE
 | 
			
		||||
 | 
			
		||||
format register_format(const std::string& name) {
 | 
			
		||||
  // Check if the format is already registered
 | 
			
		||||
  auto it = g_name_to_format.find(name);
 | 
			
		||||
  if (it != g_name_to_format.end())
 | 
			
		||||
    return it->second;
 | 
			
		||||
 | 
			
		||||
  format new_format = g_last_format++;
 | 
			
		||||
  g_name_to_format[name] = new_format;
 | 
			
		||||
  g_format_to_name[new_format] = name;
 | 
			
		||||
  return new_format;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace clip
 | 
			
		||||
@@ -1,407 +0,0 @@
 | 
			
		||||
// Clip Library
 | 
			
		||||
// Copyright (C) 2015-2024  David Capello
 | 
			
		||||
//
 | 
			
		||||
// This file is released under the terms of the MIT license.
 | 
			
		||||
// Read LICENSE.txt for more information.
 | 
			
		||||
 | 
			
		||||
#include "clip_win.h"
 | 
			
		||||
 | 
			
		||||
#include "clip.h"
 | 
			
		||||
#include "clip_lock_impl.h"
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <cstdlib>
 | 
			
		||||
#include <cstring>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// From: https://issues.chromium.org/issues/40080988#comment8
 | 
			
		||||
//
 | 
			
		||||
//  "Adds impersonation of the anonymous token around calls to the
 | 
			
		||||
//   CloseClipboard() system call. On Windows 8+ the win32k driver
 | 
			
		||||
//   captures the access token of the caller and makes it available to
 | 
			
		||||
//   other users on the desktop through the system call
 | 
			
		||||
//   GetClipboardAccessToken(). This introduces a risk of privilege
 | 
			
		||||
//   escalation in sandboxed processes. By performing the
 | 
			
		||||
//   impersonation then whenever Chrome writes data to the clipboard
 | 
			
		||||
//   only the anonymous token is available."
 | 
			
		||||
//
 | 
			
		||||
class AnonymousTokenImpersonator {
 | 
			
		||||
public:
 | 
			
		||||
  AnonymousTokenImpersonator()
 | 
			
		||||
    : m_must_revert(ImpersonateAnonymousToken(GetCurrentThread()))
 | 
			
		||||
  {}
 | 
			
		||||
 | 
			
		||||
  ~AnonymousTokenImpersonator() {
 | 
			
		||||
    if (m_must_revert)
 | 
			
		||||
      RevertToSelf();
 | 
			
		||||
  }
 | 
			
		||||
private:
 | 
			
		||||
  const bool m_must_revert;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // anonymous namespace
 | 
			
		||||
 | 
			
		||||
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) {
 | 
			
		||||
    AnonymousTokenImpersonator guard;
 | 
			
		||||
    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) ||
 | 
			
		||||
            win::wic_image_format_available(nullptr) != nullptr);
 | 
			
		||||
  }
 | 
			
		||||
#endif // CLIP_ENABLE_IMAGE
 | 
			
		||||
  else
 | 
			
		||||
    return IsClipboardFormatAvailable(f);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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_LIST_FORMATS
 | 
			
		||||
 | 
			
		||||
std::vector<format_info> lock::impl::list_formats() const {
 | 
			
		||||
  static const char* standard_formats[CF_MAX] = {
 | 
			
		||||
    "", "CF_TEXT", "CF_BITMAP", "CF_METAFILEPICT",
 | 
			
		||||
    "CF_SYLK", "CF_DIF", "CF_TIFF", "CF_OEMTEXT",
 | 
			
		||||
    "CF_DIB", "CF_PALETTE", "CF_PENDATA", "CF_RIFF",
 | 
			
		||||
    "CF_WAVE", "CF_UNICODETEXT", "CF_ENHMETAFILE", "CF_HDROP",
 | 
			
		||||
    "CF_LOCALE", "CF_DIBV5"
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  std::vector<format_info> formats;
 | 
			
		||||
  std::vector<char> format_name(512);
 | 
			
		||||
 | 
			
		||||
  formats.reserve(CountClipboardFormats());
 | 
			
		||||
 | 
			
		||||
  UINT format_id = EnumClipboardFormats(0);
 | 
			
		||||
  while (format_id != 0) {
 | 
			
		||||
    if (format_id >= CF_TEXT && format_id < CF_MAX) {
 | 
			
		||||
      // Standard clipboard format
 | 
			
		||||
      formats.emplace_back(format_id, standard_formats[format_id]);
 | 
			
		||||
    }
 | 
			
		||||
    // Get user-defined format name
 | 
			
		||||
    else {
 | 
			
		||||
      int size = GetClipboardFormatNameA(
 | 
			
		||||
        format_id,
 | 
			
		||||
        format_name.data(),
 | 
			
		||||
        format_name.size());
 | 
			
		||||
 | 
			
		||||
      formats.emplace_back(format_id, std::string(format_name.data(), size));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    format_id = EnumClipboardFormats(format_id);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return formats;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif // CLIP_ENABLE_LIST_FORMATS
 | 
			
		||||
 | 
			
		||||
#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);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Hglobal hmem(clip::win::create_dibv5(image));
 | 
			
		||||
  if (!hmem)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  SetClipboardData(CF_DIBV5, hmem);
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool lock::impl::get_image(image& output_img) const {
 | 
			
		||||
  // Tries to get the first image format that can be read using WIC
 | 
			
		||||
  // ("PNG", "JPG", "GIF", etc).
 | 
			
		||||
  UINT cbformat;
 | 
			
		||||
  if (auto read_img = win::wic_image_format_available(&cbformat)) {
 | 
			
		||||
    HANDLE handle = GetClipboardData(cbformat);
 | 
			
		||||
    if (handle) {
 | 
			
		||||
      size_t size = GlobalSize(handle);
 | 
			
		||||
      uint8_t* data = (uint8_t*)GlobalLock(handle);
 | 
			
		||||
      bool result = read_img(data, size, &output_img, nullptr);
 | 
			
		||||
      GlobalUnlock(handle);
 | 
			
		||||
      if (result)
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // If we couldn't find any, we try to use the regular DIB format.
 | 
			
		||||
  win::BitmapInfo bi;
 | 
			
		||||
  return bi.to_image(output_img);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool lock::impl::get_image_spec(image_spec& spec) const {
 | 
			
		||||
  UINT cbformat;
 | 
			
		||||
  if (auto read_img = win::wic_image_format_available(&cbformat)) {
 | 
			
		||||
    HANDLE handle = GetClipboardData(cbformat);
 | 
			
		||||
    if (handle) {
 | 
			
		||||
      size_t size = GlobalSize(handle);
 | 
			
		||||
      uint8_t* data = (uint8_t*)GlobalLock(handle);
 | 
			
		||||
      bool result = read_img(data, size, nullptr, &spec);
 | 
			
		||||
      GlobalUnlock(handle);
 | 
			
		||||
      if (result)
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  win::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
 | 
			
		||||
@@ -1,26 +0,0 @@
 | 
			
		||||
// Clip Library
 | 
			
		||||
// Copyright (c) 2024 David Capello
 | 
			
		||||
//
 | 
			
		||||
// This file is released under the terms of the MIT license.
 | 
			
		||||
// Read LICENSE.txt for more information.
 | 
			
		||||
 | 
			
		||||
#ifndef CLIP_WIN_H_INCLUDED
 | 
			
		||||
#define CLIP_WIN_H_INCLUDED
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <windows.h>
 | 
			
		||||
 | 
			
		||||
#ifndef LCS_WINDOWS_COLOR_SPACE
 | 
			
		||||
#define LCS_WINDOWS_COLOR_SPACE 'Win '
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifndef CF_DIBV5
 | 
			
		||||
#define CF_DIBV5                17
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if CLIP_ENABLE_IMAGE
 | 
			
		||||
  #include "clip_win_bmp.h"
 | 
			
		||||
  #include "clip_win_wic.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#endif // CLIP_WIN_H_INCLUDED
 | 
			
		||||
@@ -1,347 +0,0 @@
 | 
			
		||||
// Clip Library
 | 
			
		||||
// Copyright (c) 2015-2024  David Capello
 | 
			
		||||
//
 | 
			
		||||
// This file is released under the terms of the MIT license.
 | 
			
		||||
// Read LICENSE.txt for more information.
 | 
			
		||||
 | 
			
		||||
#include "clip_win_bmp.h"
 | 
			
		||||
 | 
			
		||||
#include "clip.h"
 | 
			
		||||
#include "clip_common.h"
 | 
			
		||||
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
namespace clip {
 | 
			
		||||
namespace win {
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // anonymous namespace
 | 
			
		||||
 | 
			
		||||
BitmapInfo::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 (load_from(b5))
 | 
			
		||||
      return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (IsClipboardFormatAvailable(CF_DIB)) {
 | 
			
		||||
    bi = (BITMAPINFO*)GetClipboardData(CF_DIB);
 | 
			
		||||
    load_from(bi);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool BitmapInfo::load_from(BITMAPV5HEADER* b5) {
 | 
			
		||||
  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 true;
 | 
			
		||||
  }
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool BitmapInfo::load_from(BITMAPINFO* bi) {
 | 
			
		||||
  if (!bi)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  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;
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
  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;
 | 
			
		||||
    }
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BitmapInfo::BitmapInfo(BITMAPV5HEADER* pb5) {
 | 
			
		||||
  if (load_from(pb5))
 | 
			
		||||
    b5 = pb5;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BitmapInfo::BitmapInfo(BITMAPINFO* pbi) {
 | 
			
		||||
  if (load_from(pbi))
 | 
			
		||||
    bi = pbi;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BitmapInfo::fill_spec(image_spec& spec) const {
 | 
			
		||||
  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);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool BitmapInfo::to_image(image& output_img) const {
 | 
			
		||||
  if (!is_valid()) {
 | 
			
		||||
    // There is no valid image. Maybe because there is no image at all
 | 
			
		||||
    // in the clipboard when using the BitmapInfo default
 | 
			
		||||
    // constructor. No need to report this as an error, just return
 | 
			
		||||
    // false.
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  image_spec spec;
 | 
			
		||||
  fill_spec(spec);
 | 
			
		||||
  image img(spec);
 | 
			
		||||
 | 
			
		||||
  switch (bit_count) {
 | 
			
		||||
 | 
			
		||||
    case 32:
 | 
			
		||||
    case 24:
 | 
			
		||||
    case 16: {
 | 
			
		||||
      const uint8_t* src = nullptr;
 | 
			
		||||
 | 
			
		||||
      if (compression == BI_RGB ||
 | 
			
		||||
          compression == BI_BITFIELDS) {
 | 
			
		||||
        if (b5)
 | 
			
		||||
          src = ((uint8_t*)b5) + b5->bV5Size;
 | 
			
		||||
        else
 | 
			
		||||
          src = ((uint8_t*)bi) + bi->bmiHeader.biSize;
 | 
			
		||||
        if (compression == BI_BITFIELDS)
 | 
			
		||||
          src += sizeof(RGBQUAD)*3;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (src) {
 | 
			
		||||
        const int src_bytes_per_row = spec.width*((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 (bit_count == 32 && spec.alpha_mask) {
 | 
			
		||||
        details::divide_rgb_by_alpha(img);
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    case 8: {
 | 
			
		||||
      assert(bi);
 | 
			
		||||
 | 
			
		||||
      const int colors = (bi->bmiHeader.biClrUsed > 0 ? bi->bmiHeader.biClrUsed: 256);
 | 
			
		||||
      std::vector<uint32_t> palette(colors);
 | 
			
		||||
      for (int c=0; c<colors; ++c) {
 | 
			
		||||
        palette[c] =
 | 
			
		||||
          (bi->bmiColors[c].rgbRed   << spec.red_shift) |
 | 
			
		||||
          (bi->bmiColors[c].rgbGreen << spec.green_shift) |
 | 
			
		||||
          (bi->bmiColors[c].rgbBlue  << spec.blue_shift);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      const uint8_t* src = (((uint8_t*)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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
HGLOBAL create_dibv5(const image& image) {
 | 
			
		||||
  const image_spec& spec = image.spec();
 | 
			
		||||
  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 nullptr;
 | 
			
		||||
 | 
			
		||||
  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:
 | 
			
		||||
      GlobalUnlock(hmem);
 | 
			
		||||
      GlobalFree(hmem);
 | 
			
		||||
 | 
			
		||||
      error_handler e = get_error_handler();
 | 
			
		||||
      if (e)
 | 
			
		||||
        e(ErrorCode::ImageNotSupported);
 | 
			
		||||
      return nullptr;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  GlobalUnlock(hmem);
 | 
			
		||||
  return hmem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace win
 | 
			
		||||
} // namespace clip
 | 
			
		||||
@@ -1,66 +0,0 @@
 | 
			
		||||
// Clip Library
 | 
			
		||||
// Copyright (c) 2015-2024  David Capello
 | 
			
		||||
//
 | 
			
		||||
// This file is released under the terms of the MIT license.
 | 
			
		||||
// Read LICENSE.txt for more information.
 | 
			
		||||
 | 
			
		||||
#ifndef CLIP_WIN_BMP_H_INCLUDED
 | 
			
		||||
#define CLIP_WIN_BMP_H_INCLUDED
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#if !CLIP_ENABLE_IMAGE
 | 
			
		||||
  #error This file can be include only when CLIP_ENABLE_IMAGE is defined
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
 | 
			
		||||
#include <windows.h>
 | 
			
		||||
 | 
			
		||||
namespace clip {
 | 
			
		||||
 | 
			
		||||
class image;
 | 
			
		||||
struct image_spec;
 | 
			
		||||
 | 
			
		||||
namespace win {
 | 
			
		||||
 | 
			
		||||
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();
 | 
			
		||||
  explicit BitmapInfo(BITMAPV5HEADER* pb5);
 | 
			
		||||
  explicit BitmapInfo(BITMAPINFO* pbi);
 | 
			
		||||
 | 
			
		||||
  bool is_valid() const {
 | 
			
		||||
    return (b5 || bi);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void fill_spec(image_spec& spec) const;
 | 
			
		||||
 | 
			
		||||
  // Fills the output_img with the data provided by this
 | 
			
		||||
  // BitmapInfo. Returns true if it was able to fill the output image
 | 
			
		||||
  // or false otherwise.
 | 
			
		||||
  bool to_image(image& output_img) const;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
  bool load_from(BITMAPV5HEADER* b5);
 | 
			
		||||
  bool load_from(BITMAPINFO* bi);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Returns a handle to the HGLOBAL memory reserved to create a DIBV5
 | 
			
		||||
// based on the image passed by parameter. Returns null if it cannot
 | 
			
		||||
// create the handle.
 | 
			
		||||
HGLOBAL create_dibv5(const image& image);
 | 
			
		||||
 | 
			
		||||
} // namespace win
 | 
			
		||||
} // namespace clip
 | 
			
		||||
 | 
			
		||||
#endif // CLIP_WIN_BMP_H_INCLUDED
 | 
			
		||||
@@ -1,472 +0,0 @@
 | 
			
		||||
// Clip Library
 | 
			
		||||
// Copyright (c) 2020-2024 David Capello
 | 
			
		||||
//
 | 
			
		||||
// This file is released under the terms of the MIT license.
 | 
			
		||||
// Read LICENSE.txt for more information.
 | 
			
		||||
 | 
			
		||||
#include "clip_win_wic.h"
 | 
			
		||||
 | 
			
		||||
#include "clip.h"
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include <shlwapi.h>
 | 
			
		||||
#include <wincodec.h>
 | 
			
		||||
 | 
			
		||||
namespace clip {
 | 
			
		||||
namespace win {
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
 | 
			
		||||
// Successful calls to CoInitialize() (S_OK or S_FALSE) must match
 | 
			
		||||
// the calls to CoUninitialize().
 | 
			
		||||
// From: https://docs.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-couninitialize#remarks
 | 
			
		||||
struct coinit {
 | 
			
		||||
  HRESULT hr;
 | 
			
		||||
  coinit() {
 | 
			
		||||
    hr = CoInitialize(nullptr);
 | 
			
		||||
  }
 | 
			
		||||
  ~coinit() {
 | 
			
		||||
    if (hr == S_OK || hr == S_FALSE)
 | 
			
		||||
      CoUninitialize();
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template<class T>
 | 
			
		||||
class comptr {
 | 
			
		||||
public:
 | 
			
		||||
  comptr() { }
 | 
			
		||||
  explicit comptr(T* ptr) : m_ptr(ptr) { }
 | 
			
		||||
  comptr(const comptr&) = delete;
 | 
			
		||||
  comptr& operator=(const comptr&) = delete;
 | 
			
		||||
  ~comptr() { reset(); }
 | 
			
		||||
 | 
			
		||||
  T** operator&() { return &m_ptr; }
 | 
			
		||||
  T* operator->() { return m_ptr; }
 | 
			
		||||
  bool operator!() const { return !m_ptr; }
 | 
			
		||||
 | 
			
		||||
  T* get() { return m_ptr; }
 | 
			
		||||
  void reset() {
 | 
			
		||||
    if (m_ptr) {
 | 
			
		||||
      m_ptr->Release();
 | 
			
		||||
      m_ptr = nullptr;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
private:
 | 
			
		||||
  T* m_ptr = nullptr;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#ifdef CLIP_SUPPORT_WINXP
 | 
			
		||||
class hmodule {
 | 
			
		||||
public:
 | 
			
		||||
  hmodule(LPCWSTR name) : m_ptr(LoadLibraryW(name)) { }
 | 
			
		||||
  hmodule(const hmodule&) = delete;
 | 
			
		||||
  hmodule& operator=(const hmodule&) = delete;
 | 
			
		||||
  ~hmodule() {
 | 
			
		||||
    if (m_ptr)
 | 
			
		||||
      FreeLibrary(m_ptr);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  operator HMODULE() { return m_ptr; }
 | 
			
		||||
  bool operator!() const { return !m_ptr; }
 | 
			
		||||
private:
 | 
			
		||||
  HMODULE m_ptr = nullptr;
 | 
			
		||||
};
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
struct WicImageFormat {
 | 
			
		||||
  const char* names[3];        // Alternative names of this format
 | 
			
		||||
  UINT ids[3];                 // Clipboard format ID for each name of this format
 | 
			
		||||
  ReadWicImageFormatFunc read; // Function used to decode data in this format
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
WicImageFormat wic_image_formats[] = {
 | 
			
		||||
  { { "PNG", "image/png",  nullptr }, { 0, 0, 0 }, read_png },
 | 
			
		||||
  { { "JPG", "image/jpeg", "JPEG"  }, { 0, 0, 0 }, read_jpg },
 | 
			
		||||
  { { "BMP", "image/bmp",  nullptr }, { 0, 0, 0 }, read_bmp },
 | 
			
		||||
  { { "GIF", "image/gif",  nullptr }, { 0, 0, 0 }, read_gif }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // anonymous namespace
 | 
			
		||||
 | 
			
		||||
ReadWicImageFormatFunc wic_image_format_available(UINT* output_cbformat) {
 | 
			
		||||
  for (auto& fmt : wic_image_formats) {
 | 
			
		||||
    for (int i=0; i<3; ++i) {
 | 
			
		||||
      const char* name = fmt.names[i];
 | 
			
		||||
      if (!name)
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      // Although RegisterClipboardFormatA() already returns the same
 | 
			
		||||
      // value for the same "name" (even for different apps), we
 | 
			
		||||
      // prefer to cache the value to avoid calling
 | 
			
		||||
      // RegisterClipboardFormatA() several times (as internally that
 | 
			
		||||
      // function must do some kind of hash map name -> ID
 | 
			
		||||
      // conversion).
 | 
			
		||||
      UINT cbformat = fmt.ids[i];
 | 
			
		||||
      if (cbformat == 0)
 | 
			
		||||
        fmt.ids[i] = cbformat = RegisterClipboardFormatA(name);
 | 
			
		||||
 | 
			
		||||
      if (cbformat && IsClipboardFormatAvailable(cbformat)) {
 | 
			
		||||
        if (output_cbformat)
 | 
			
		||||
          *output_cbformat = cbformat;
 | 
			
		||||
        return fmt.read;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//////////////////////////////////////////////////////////////////////
 | 
			
		||||
// Encode the image as PNG format
 | 
			
		||||
 | 
			
		||||
bool write_png_on_stream(const image& image,
 | 
			
		||||
                         IStream* stream) {
 | 
			
		||||
  const image_spec& spec = image.spec();
 | 
			
		||||
 | 
			
		||||
  comptr<IWICBitmapEncoder> encoder;
 | 
			
		||||
  HRESULT hr = CoCreateInstance(CLSID_WICPngEncoder,
 | 
			
		||||
                                nullptr, CLSCTX_INPROC_SERVER,
 | 
			
		||||
                                IID_PPV_ARGS(&encoder));
 | 
			
		||||
  if (FAILED(hr))
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  hr = encoder->Initialize(stream, WICBitmapEncoderNoCache);
 | 
			
		||||
  if (FAILED(hr))
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  comptr<IWICBitmapFrameEncode> frame;
 | 
			
		||||
  comptr<IPropertyBag2> options;
 | 
			
		||||
  hr = encoder->CreateNewFrame(&frame, &options);
 | 
			
		||||
  if (FAILED(hr))
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  hr = frame->Initialize(options.get());
 | 
			
		||||
  if (FAILED(hr))
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  // PNG encoder (and decoder) only supports GUID_WICPixelFormat32bppBGRA for 32bpp.
 | 
			
		||||
  // See: https://docs.microsoft.com/en-us/windows/win32/wic/-wic-codec-native-pixel-formats#png-native-codec
 | 
			
		||||
  WICPixelFormatGUID pixelFormat = GUID_WICPixelFormat32bppBGRA;
 | 
			
		||||
  hr = frame->SetPixelFormat(&pixelFormat);
 | 
			
		||||
  if (FAILED(hr))
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  hr = frame->SetSize(spec.width, spec.height);
 | 
			
		||||
  if (FAILED(hr))
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  std::vector<uint32_t> buf;
 | 
			
		||||
  uint8_t* ptr = (uint8_t*)image.data();
 | 
			
		||||
  int bytes_per_row = spec.bytes_per_row;
 | 
			
		||||
 | 
			
		||||
  // Convert to GUID_WICPixelFormat32bppBGRA if needed
 | 
			
		||||
  if (spec.red_mask != 0xff0000 ||
 | 
			
		||||
      spec.green_mask != 0xff00 ||
 | 
			
		||||
      spec.blue_mask != 0xff ||
 | 
			
		||||
      spec.alpha_mask != 0xff000000) {
 | 
			
		||||
    buf.resize(spec.width * spec.height);
 | 
			
		||||
    uint32_t* dst = (uint32_t*)&buf[0];
 | 
			
		||||
    uint32_t* src = (uint32_t*)image.data();
 | 
			
		||||
    for (int y=0; y<spec.height; ++y) {
 | 
			
		||||
      auto src_line_start = src;
 | 
			
		||||
      for (int x=0; x<spec.width; ++x) {
 | 
			
		||||
        uint32_t c = *src;
 | 
			
		||||
        *dst = ((((c & spec.red_mask  ) >> spec.red_shift  ) << 16) |
 | 
			
		||||
                (((c & spec.green_mask) >> spec.green_shift) <<  8) |
 | 
			
		||||
                (((c & spec.blue_mask ) >> spec.blue_shift )      ) |
 | 
			
		||||
                (((c & spec.alpha_mask) >> spec.alpha_shift) << 24));
 | 
			
		||||
        ++dst;
 | 
			
		||||
        ++src;
 | 
			
		||||
      }
 | 
			
		||||
      src = (uint32_t*)(((uint8_t*)src_line_start) + spec.bytes_per_row);
 | 
			
		||||
    }
 | 
			
		||||
    ptr = (uint8_t*)&buf[0];
 | 
			
		||||
    bytes_per_row = 4 * spec.width;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  hr = frame->WritePixels(spec.height,
 | 
			
		||||
                          bytes_per_row,
 | 
			
		||||
                          bytes_per_row * spec.height,
 | 
			
		||||
                          (BYTE*)ptr);
 | 
			
		||||
  if (FAILED(hr))
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  hr = frame->Commit();
 | 
			
		||||
  if (FAILED(hr))
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  hr = encoder->Commit();
 | 
			
		||||
  if (FAILED(hr))
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
HGLOBAL write_png(const image& image) {
 | 
			
		||||
  coinit com;
 | 
			
		||||
 | 
			
		||||
  comptr<IStream> stream;
 | 
			
		||||
  HRESULT hr = CreateStreamOnHGlobal(nullptr, false, &stream);
 | 
			
		||||
  if (FAILED(hr))
 | 
			
		||||
    return nullptr;
 | 
			
		||||
 | 
			
		||||
  bool result = write_png_on_stream(image, stream.get());
 | 
			
		||||
 | 
			
		||||
  HGLOBAL handle;
 | 
			
		||||
  hr = GetHGlobalFromStream(stream.get(), &handle);
 | 
			
		||||
  if (result)
 | 
			
		||||
    return handle;
 | 
			
		||||
 | 
			
		||||
  GlobalFree(handle);
 | 
			
		||||
  return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
IStream* create_stream(const BYTE* pInit, UINT cbInit)
 | 
			
		||||
{
 | 
			
		||||
#ifdef CLIP_SUPPORT_WINXP
 | 
			
		||||
  // Pull SHCreateMemStream from shlwapi.dll by ordinal 12
 | 
			
		||||
  // for Windows XP support
 | 
			
		||||
  // From: https://learn.microsoft.com/en-us/windows/win32/api/shlwapi/nf-shlwapi-shcreatememstream#remarks
 | 
			
		||||
 | 
			
		||||
  typedef IStream*(WINAPI * SHCreateMemStreamPtr)(const BYTE* pInit,
 | 
			
		||||
                                                  UINT cbInit);
 | 
			
		||||
  hmodule shlwapiDll(L"shlwapi.dll");
 | 
			
		||||
  if (!shlwapiDll)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  auto SHCreateMemStream = reinterpret_cast<SHCreateMemStreamPtr>(
 | 
			
		||||
    GetProcAddress(shlwapiDll, (LPCSTR)12));
 | 
			
		||||
  if (!SHCreateMemStream)
 | 
			
		||||
    return false;
 | 
			
		||||
#endif
 | 
			
		||||
  return SHCreateMemStream(pInit, cbInit);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
image_spec spec_from_pixelformat(const WICPixelFormatGUID& pixelFormat, unsigned long w, unsigned long h)
 | 
			
		||||
{
 | 
			
		||||
  image_spec spec;
 | 
			
		||||
  spec.width = w;
 | 
			
		||||
  spec.height = h;
 | 
			
		||||
  if (pixelFormat == GUID_WICPixelFormat32bppBGRA ||
 | 
			
		||||
      pixelFormat == GUID_WICPixelFormat32bppBGR) {
 | 
			
		||||
    spec.bits_per_pixel = 32;
 | 
			
		||||
    spec.red_mask = 0xff0000;
 | 
			
		||||
    spec.green_mask = 0xff00;
 | 
			
		||||
    spec.blue_mask = 0xff;
 | 
			
		||||
    spec.alpha_mask = 0xff000000;
 | 
			
		||||
    spec.red_shift = 16;
 | 
			
		||||
    spec.green_shift = 8;
 | 
			
		||||
    spec.blue_shift = 0;
 | 
			
		||||
    spec.alpha_shift = 24;
 | 
			
		||||
    // Reset mask and shift for BGR pixel format.
 | 
			
		||||
    if (pixelFormat == GUID_WICPixelFormat32bppBGR) {
 | 
			
		||||
      spec.alpha_mask = 0;
 | 
			
		||||
      spec.alpha_shift = 0;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  else if (pixelFormat == GUID_WICPixelFormat24bppBGR ||
 | 
			
		||||
           pixelFormat == GUID_WICPixelFormat8bppIndexed) {
 | 
			
		||||
    spec.bits_per_pixel = 24;
 | 
			
		||||
    spec.red_mask = 0xff0000;
 | 
			
		||||
    spec.green_mask = 0xff00;
 | 
			
		||||
    spec.blue_mask = 0xff;
 | 
			
		||||
    spec.alpha_mask = 0;
 | 
			
		||||
    spec.red_shift = 16;
 | 
			
		||||
    spec.green_shift = 8;
 | 
			
		||||
    spec.blue_shift = 0;
 | 
			
		||||
    spec.alpha_shift = 0;
 | 
			
		||||
  }
 | 
			
		||||
  spec.bytes_per_row = ((w*spec.bits_per_pixel+31) / 32) * 4;
 | 
			
		||||
  return spec;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Tries to decode the input buf of size len using the specified
 | 
			
		||||
// decoders. If output_image is not null, the decoded image is
 | 
			
		||||
// returned there, if output_spec is not null then the image
 | 
			
		||||
// specifications are set there.
 | 
			
		||||
bool decode(const GUID decoder_clsid1,
 | 
			
		||||
            const GUID decoder_clsid2,
 | 
			
		||||
            const uint8_t* buf,
 | 
			
		||||
            const UINT len,
 | 
			
		||||
            image* output_image,
 | 
			
		||||
            image_spec* output_spec)
 | 
			
		||||
{
 | 
			
		||||
  coinit com;
 | 
			
		||||
 | 
			
		||||
  comptr<IWICBitmapDecoder> decoder;
 | 
			
		||||
  HRESULT hr = CoCreateInstance(decoder_clsid1, nullptr,
 | 
			
		||||
                                CLSCTX_INPROC_SERVER,
 | 
			
		||||
                                IID_PPV_ARGS(&decoder));
 | 
			
		||||
  if (FAILED(hr) && decoder_clsid2 != GUID_NULL) {
 | 
			
		||||
    hr = CoCreateInstance(decoder_clsid2, nullptr,
 | 
			
		||||
                          CLSCTX_INPROC_SERVER,
 | 
			
		||||
                          IID_PPV_ARGS(&decoder));
 | 
			
		||||
  }
 | 
			
		||||
  if (FAILED(hr))
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  // Can decoder be nullptr if hr is S_OK/successful? We've received
 | 
			
		||||
  // some crash reports that might indicate this.
 | 
			
		||||
  if (!decoder)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  comptr<IStream> stream(create_stream(buf, len));
 | 
			
		||||
  if (!stream)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  hr = decoder->Initialize(stream.get(), WICDecodeMetadataCacheOnDemand);
 | 
			
		||||
  if (FAILED(hr))
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  comptr<IWICBitmapFrameDecode> frame;
 | 
			
		||||
  hr = decoder->GetFrame(0, &frame);
 | 
			
		||||
  if (FAILED(hr))
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  WICPixelFormatGUID pixelFormat;
 | 
			
		||||
  hr = frame->GetPixelFormat(&pixelFormat);
 | 
			
		||||
  if (FAILED(hr))
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  // Only support these pixel formats
 | 
			
		||||
  // TODO add support for more pixel formats
 | 
			
		||||
  if (pixelFormat != GUID_WICPixelFormat32bppBGRA &&
 | 
			
		||||
      pixelFormat != GUID_WICPixelFormat32bppBGR &&
 | 
			
		||||
      pixelFormat != GUID_WICPixelFormat24bppBGR &&
 | 
			
		||||
      pixelFormat != GUID_WICPixelFormat8bppIndexed)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  UINT width = 0, height = 0;
 | 
			
		||||
  hr = frame->GetSize(&width, &height);
 | 
			
		||||
  if (FAILED(hr))
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  image_spec spec = spec_from_pixelformat(pixelFormat, width, height);
 | 
			
		||||
 | 
			
		||||
  if (output_spec)
 | 
			
		||||
    *output_spec = spec;
 | 
			
		||||
 | 
			
		||||
  image img;
 | 
			
		||||
  if (output_image) {
 | 
			
		||||
    if (pixelFormat == GUID_WICPixelFormat8bppIndexed) {
 | 
			
		||||
      std::vector<BYTE> pixels(spec.width * spec.height);
 | 
			
		||||
      hr = frame->CopyPixels(nullptr,  // Entire bitmap
 | 
			
		||||
                             spec.width,
 | 
			
		||||
                             spec.width * spec.height,
 | 
			
		||||
                             pixels.data());
 | 
			
		||||
 | 
			
		||||
      if (FAILED(hr))
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
      comptr<IWICImagingFactory> factory;
 | 
			
		||||
      HRESULT hr = CoCreateInstance(CLSID_WICImagingFactory,
 | 
			
		||||
                                    nullptr,
 | 
			
		||||
                                    CLSCTX_INPROC_SERVER,
 | 
			
		||||
                                    IID_PPV_ARGS(&factory));
 | 
			
		||||
      if (FAILED(hr))
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
      comptr<IWICPalette> palette;
 | 
			
		||||
      hr = factory->CreatePalette(&palette);
 | 
			
		||||
      if (FAILED(hr))
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
      hr = frame->CopyPalette(palette.get());
 | 
			
		||||
      if (FAILED(hr))
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
      UINT numcolors;
 | 
			
		||||
      hr = palette->GetColorCount(&numcolors);
 | 
			
		||||
      if (FAILED(hr))
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
      UINT actualNumcolors;
 | 
			
		||||
      std::vector<WICColor> colors(numcolors);
 | 
			
		||||
      hr = palette->GetColors(numcolors, colors.data(), &actualNumcolors);
 | 
			
		||||
      if (FAILED(hr))
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
      BOOL hasAlpha = false;
 | 
			
		||||
      palette->HasAlpha(&hasAlpha);
 | 
			
		||||
      if (hasAlpha) {
 | 
			
		||||
        spec = spec_from_pixelformat(GUID_WICPixelFormat32bppBGRA, width, height);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      img = image(spec);
 | 
			
		||||
      char* dst = img.data();
 | 
			
		||||
      BYTE* src = pixels.data();
 | 
			
		||||
      for (int y = 0; y < spec.height; ++y) {
 | 
			
		||||
        char* dst_x = dst;
 | 
			
		||||
        for (int x = 0; x < spec.width; ++x, dst_x+=spec.bits_per_pixel/8, ++src) {
 | 
			
		||||
          *((uint32_t*)dst_x) = (*src < numcolors ? colors[*src] : 0);
 | 
			
		||||
        }
 | 
			
		||||
        dst += spec.bytes_per_row;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      img = image(spec);
 | 
			
		||||
      hr = frame->CopyPixels(nullptr,  // Entire bitmap
 | 
			
		||||
                             spec.bytes_per_row,
 | 
			
		||||
                             spec.bytes_per_row * spec.height,
 | 
			
		||||
                             (BYTE*)img.data());
 | 
			
		||||
      if (FAILED(hr))
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    std::swap(*output_image, img);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//////////////////////////////////////////////////////////////////////
 | 
			
		||||
// Decode the clipboard data from PNG format
 | 
			
		||||
 | 
			
		||||
bool read_png(const uint8_t* buf,
 | 
			
		||||
              const UINT len,
 | 
			
		||||
              image* output_image,
 | 
			
		||||
              image_spec* output_spec) {
 | 
			
		||||
  return decode(CLSID_WICPngDecoder2, CLSID_WICPngDecoder1,
 | 
			
		||||
                buf, len, output_image, output_spec);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//////////////////////////////////////////////////////////////////////
 | 
			
		||||
// Decode the clipboard data from JPEG format
 | 
			
		||||
 | 
			
		||||
bool read_jpg(const uint8_t* buf,
 | 
			
		||||
              const UINT len,
 | 
			
		||||
              image* output_image,
 | 
			
		||||
              image_spec* output_spec)
 | 
			
		||||
{
 | 
			
		||||
  return decode(CLSID_WICJpegDecoder, GUID_NULL,
 | 
			
		||||
                buf, len, output_image, output_spec);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//////////////////////////////////////////////////////////////////////
 | 
			
		||||
// Decode the clipboard data from GIF format
 | 
			
		||||
 | 
			
		||||
bool read_gif(const uint8_t* buf,
 | 
			
		||||
              const UINT len,
 | 
			
		||||
              image* output_image,
 | 
			
		||||
              image_spec* output_spec)
 | 
			
		||||
{
 | 
			
		||||
  return decode(CLSID_WICGifDecoder, GUID_NULL,
 | 
			
		||||
                buf, len, output_image, output_spec);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//////////////////////////////////////////////////////////////////////
 | 
			
		||||
// Decode the clipboard data from BMP format
 | 
			
		||||
 | 
			
		||||
bool read_bmp(const uint8_t* buf,
 | 
			
		||||
              const UINT len,
 | 
			
		||||
              image* output_image,
 | 
			
		||||
              image_spec* output_spec)
 | 
			
		||||
{
 | 
			
		||||
  return decode(CLSID_WICBmpDecoder, GUID_NULL,
 | 
			
		||||
                buf, len, output_image, output_spec);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace win
 | 
			
		||||
} // namespace clip
 | 
			
		||||
@@ -1,76 +0,0 @@
 | 
			
		||||
// Clip Library
 | 
			
		||||
// Copyright (c) 2020-2024 David Capello
 | 
			
		||||
//
 | 
			
		||||
// This file is released under the terms of the MIT license.
 | 
			
		||||
// Read LICENSE.txt for more information.
 | 
			
		||||
 | 
			
		||||
#ifndef CLIP_WIN_WIC_H_INCLUDED
 | 
			
		||||
#define CLIP_WIN_WIC_H_INCLUDED
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#if !CLIP_ENABLE_IMAGE
 | 
			
		||||
  #error This file can be include only when CLIP_ENABLE_IMAGE is defined
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
 | 
			
		||||
#include <windows.h>
 | 
			
		||||
 | 
			
		||||
namespace clip {
 | 
			
		||||
 | 
			
		||||
class image;
 | 
			
		||||
struct image_spec;
 | 
			
		||||
 | 
			
		||||
namespace win {
 | 
			
		||||
 | 
			
		||||
typedef bool (*ReadWicImageFormatFunc)(const uint8_t*,
 | 
			
		||||
                                       const UINT,
 | 
			
		||||
                                       clip::image*,
 | 
			
		||||
                                       clip::image_spec*);
 | 
			
		||||
 | 
			
		||||
ReadWicImageFormatFunc wic_image_format_available(UINT* output_cbformat);
 | 
			
		||||
 | 
			
		||||
//////////////////////////////////////////////////////////////////////
 | 
			
		||||
// Encode the image as PNG format
 | 
			
		||||
 | 
			
		||||
bool write_png_on_stream(const image& image, IStream* stream);
 | 
			
		||||
 | 
			
		||||
HGLOBAL write_png(const image& image);
 | 
			
		||||
 | 
			
		||||
//////////////////////////////////////////////////////////////////////
 | 
			
		||||
// Decode the clipboard data from PNG format
 | 
			
		||||
 | 
			
		||||
bool read_png(const uint8_t* buf,
 | 
			
		||||
              const UINT len,
 | 
			
		||||
              image* output_image,
 | 
			
		||||
              image_spec* output_spec);
 | 
			
		||||
 | 
			
		||||
//////////////////////////////////////////////////////////////////////
 | 
			
		||||
// Decode the clipboard data from JPEG format
 | 
			
		||||
 | 
			
		||||
bool read_jpg(const uint8_t* buf,
 | 
			
		||||
              const UINT len,
 | 
			
		||||
              image* output_image,
 | 
			
		||||
              image_spec* output_spec);
 | 
			
		||||
 | 
			
		||||
//////////////////////////////////////////////////////////////////////
 | 
			
		||||
// Decode the clipboard data from GIF format
 | 
			
		||||
 | 
			
		||||
bool read_gif(const uint8_t* buf,
 | 
			
		||||
              const UINT len,
 | 
			
		||||
              image* output_image,
 | 
			
		||||
              image_spec* output_spec);
 | 
			
		||||
 | 
			
		||||
//////////////////////////////////////////////////////////////////////
 | 
			
		||||
// Decode the clipboard data from BMP format
 | 
			
		||||
 | 
			
		||||
bool read_bmp(const uint8_t* buf,
 | 
			
		||||
              const UINT len,
 | 
			
		||||
              image* output_image,
 | 
			
		||||
              image_spec* output_spec);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
} // namespace win
 | 
			
		||||
} // namespace clip
 | 
			
		||||
 | 
			
		||||
#endif // CLIP_WIN_WIC_H_INCLUDED
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,230 +0,0 @@
 | 
			
		||||
// Clip Library
 | 
			
		||||
// Copyright (c) 2018-2021 David Capello
 | 
			
		||||
//
 | 
			
		||||
// This file is released under the terms of the MIT license.
 | 
			
		||||
// Read LICENSE.txt for more information.
 | 
			
		||||
 | 
			
		||||
#include "clip.h"
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include "png.h"
 | 
			
		||||
 | 
			
		||||
namespace clip {
 | 
			
		||||
namespace x11 {
 | 
			
		||||
 | 
			
		||||
//////////////////////////////////////////////////////////////////////
 | 
			
		||||
// Functions to convert clip::image into png data to store it in the
 | 
			
		||||
// clipboard.
 | 
			
		||||
 | 
			
		||||
void write_data_fn(png_structp png, png_bytep buf, png_size_t len) {
 | 
			
		||||
  std::vector<uint8_t>& output = *(std::vector<uint8_t>*)png_get_io_ptr(png);
 | 
			
		||||
  const size_t i = output.size();
 | 
			
		||||
  output.resize(i+len);
 | 
			
		||||
  std::copy(buf, buf+len, output.begin()+i);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool write_png(const image& image,
 | 
			
		||||
               std::vector<uint8_t>& output) {
 | 
			
		||||
  png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING,
 | 
			
		||||
                                            nullptr, nullptr, nullptr);
 | 
			
		||||
  if (!png)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  png_infop info = png_create_info_struct(png);
 | 
			
		||||
  if (!info) {
 | 
			
		||||
    png_destroy_write_struct(&png, nullptr);
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (setjmp(png_jmpbuf(png))) {
 | 
			
		||||
    png_destroy_write_struct(&png, &info);
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  png_set_write_fn(png,
 | 
			
		||||
                   (png_voidp)&output,
 | 
			
		||||
                   write_data_fn,
 | 
			
		||||
                   nullptr);    // No need for a flush function
 | 
			
		||||
 | 
			
		||||
  const image_spec& spec = image.spec();
 | 
			
		||||
  int color_type = (spec.alpha_mask ?
 | 
			
		||||
                    PNG_COLOR_TYPE_RGB_ALPHA:
 | 
			
		||||
                    PNG_COLOR_TYPE_RGB);
 | 
			
		||||
 | 
			
		||||
  png_set_IHDR(png, info,
 | 
			
		||||
               spec.width, spec.height, 8, color_type,
 | 
			
		||||
               PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
 | 
			
		||||
  png_write_info(png, info);
 | 
			
		||||
  png_set_packing(png);
 | 
			
		||||
 | 
			
		||||
  png_bytep row =
 | 
			
		||||
    (png_bytep)png_malloc(png, png_get_rowbytes(png, info));
 | 
			
		||||
 | 
			
		||||
  for (png_uint_32 y=0; y<spec.height; ++y) {
 | 
			
		||||
    const uint32_t* src =
 | 
			
		||||
      (const uint32_t*)(((const uint8_t*)image.data())
 | 
			
		||||
                        + y*spec.bytes_per_row);
 | 
			
		||||
    uint8_t* dst = row;
 | 
			
		||||
    unsigned int x, c;
 | 
			
		||||
 | 
			
		||||
    for (x=0; x<spec.width; x++) {
 | 
			
		||||
      c = *(src++);
 | 
			
		||||
      *(dst++) = (c & spec.red_mask  ) >> spec.red_shift;
 | 
			
		||||
      *(dst++) = (c & spec.green_mask) >> spec.green_shift;
 | 
			
		||||
      *(dst++) = (c & spec.blue_mask ) >> spec.blue_shift;
 | 
			
		||||
      if (color_type == PNG_COLOR_TYPE_RGB_ALPHA)
 | 
			
		||||
        *(dst++) = (c & spec.alpha_mask) >> spec.alpha_shift;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    png_write_rows(png, &row, 1);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  png_free(png, row);
 | 
			
		||||
  png_write_end(png, info);
 | 
			
		||||
  png_destroy_write_struct(&png, &info);
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//////////////////////////////////////////////////////////////////////
 | 
			
		||||
// Functions to convert png data stored in the clipboard to a
 | 
			
		||||
// clip::image.
 | 
			
		||||
 | 
			
		||||
struct read_png_io {
 | 
			
		||||
  const uint8_t* buf;
 | 
			
		||||
  size_t len;
 | 
			
		||||
  size_t pos;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void read_data_fn(png_structp png, png_bytep buf, png_size_t len) {
 | 
			
		||||
  read_png_io& io = *(read_png_io*)png_get_io_ptr(png);
 | 
			
		||||
  if (io.pos < io.len) {
 | 
			
		||||
    size_t n = std::min(len, io.len-io.pos);
 | 
			
		||||
    if (n > 0) {
 | 
			
		||||
      std::copy(io.buf+io.pos,
 | 
			
		||||
                io.buf+io.pos+n,
 | 
			
		||||
                buf);
 | 
			
		||||
      io.pos += n;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool read_png(const uint8_t* buf,
 | 
			
		||||
              const size_t len,
 | 
			
		||||
              image* output_image,
 | 
			
		||||
              image_spec* output_spec) {
 | 
			
		||||
  png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING,
 | 
			
		||||
                                           nullptr, nullptr, nullptr);
 | 
			
		||||
  if (!png)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  png_infop info = png_create_info_struct(png);
 | 
			
		||||
  if (!info) {
 | 
			
		||||
    png_destroy_read_struct(&png, nullptr, nullptr);
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (setjmp(png_jmpbuf(png))) {
 | 
			
		||||
    png_destroy_read_struct(&png, &info, nullptr);
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  read_png_io io = { buf, len, 0 };
 | 
			
		||||
  png_set_read_fn(png, (png_voidp)&io, read_data_fn);
 | 
			
		||||
 | 
			
		||||
  png_read_info(png, info);
 | 
			
		||||
 | 
			
		||||
  png_uint_32 width, height;
 | 
			
		||||
  int bit_depth, color_type, interlace_type;
 | 
			
		||||
  png_get_IHDR(png, info, &width, &height,
 | 
			
		||||
               &bit_depth, &color_type,
 | 
			
		||||
               &interlace_type,
 | 
			
		||||
               nullptr, nullptr);
 | 
			
		||||
 | 
			
		||||
  image_spec spec;
 | 
			
		||||
  spec.width = width;
 | 
			
		||||
  spec.height = height;
 | 
			
		||||
  spec.bits_per_pixel = 32;
 | 
			
		||||
 | 
			
		||||
  // Don't use png_get_rowbytes(png, info) here because this is the
 | 
			
		||||
  // bytes_per_row of the output clip::image (the png file could
 | 
			
		||||
  // contain 24bpp but we want to return a 32bpp anyway with alpha=255
 | 
			
		||||
  // in that case).
 | 
			
		||||
  spec.bytes_per_row = 4*width;
 | 
			
		||||
 | 
			
		||||
  spec.red_mask    = 0x000000ff;
 | 
			
		||||
  spec.green_mask  = 0x0000ff00;
 | 
			
		||||
  spec.blue_mask   = 0x00ff0000;
 | 
			
		||||
  spec.red_shift   = 0;
 | 
			
		||||
  spec.green_shift = 8;
 | 
			
		||||
  spec.blue_shift  = 16;
 | 
			
		||||
 | 
			
		||||
  if ((color_type & PNG_COLOR_MASK_ALPHA) == PNG_COLOR_MASK_ALPHA) {
 | 
			
		||||
    spec.alpha_mask = 0xff000000;
 | 
			
		||||
    spec.alpha_shift = 24;
 | 
			
		||||
  }
 | 
			
		||||
  else {
 | 
			
		||||
    spec.alpha_mask = 0;
 | 
			
		||||
    spec.alpha_shift = 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (output_spec)
 | 
			
		||||
    *output_spec = spec;
 | 
			
		||||
 | 
			
		||||
  if (output_image &&
 | 
			
		||||
      width > 0 &&
 | 
			
		||||
      height > 0) {
 | 
			
		||||
    image img(spec);
 | 
			
		||||
 | 
			
		||||
    // We want RGB 24-bit or RGBA 32-bit as a result
 | 
			
		||||
    png_set_strip_16(png); // Down to 8-bit (TODO we might support 16-bit values)
 | 
			
		||||
    png_set_packing(png);  // Use one byte if color depth < 8-bit
 | 
			
		||||
    png_set_expand_gray_1_2_4_to_8(png);
 | 
			
		||||
    png_set_palette_to_rgb(png);
 | 
			
		||||
    png_set_gray_to_rgb(png);
 | 
			
		||||
    png_set_tRNS_to_alpha(png);
 | 
			
		||||
 | 
			
		||||
    int number_passes = png_set_interlace_handling(png);
 | 
			
		||||
    png_read_update_info(png, info);
 | 
			
		||||
 | 
			
		||||
    const int src_bytes_per_row = png_get_rowbytes(png, info);
 | 
			
		||||
    png_bytepp rows = (png_bytepp)png_malloc(png, sizeof(png_bytep)*height);
 | 
			
		||||
    png_uint_32 y;
 | 
			
		||||
    for (y=0; y<height; ++y)
 | 
			
		||||
      rows[y] = (png_bytep)png_malloc(png, src_bytes_per_row);
 | 
			
		||||
 | 
			
		||||
    for (int pass=0; pass<number_passes; ++pass)
 | 
			
		||||
      for (y=0; y<height; ++y)
 | 
			
		||||
        png_read_rows(png, rows+y, nullptr, 1);
 | 
			
		||||
 | 
			
		||||
    for (y=0; y<height; ++y) {
 | 
			
		||||
      const uint8_t* src = rows[y];
 | 
			
		||||
      uint32_t* dst = (uint32_t*)(img.data() + y*spec.bytes_per_row);
 | 
			
		||||
      unsigned int x, r, g, b, a = 0;
 | 
			
		||||
 | 
			
		||||
      for (x=0; x<width; x++) {
 | 
			
		||||
        r = *(src++);
 | 
			
		||||
        g = *(src++);
 | 
			
		||||
        b = *(src++);
 | 
			
		||||
        if (spec.alpha_mask)
 | 
			
		||||
          a = *(src++);
 | 
			
		||||
        *(dst++) =
 | 
			
		||||
          (r << spec.red_shift) |
 | 
			
		||||
          (g << spec.green_shift) |
 | 
			
		||||
          (b << spec.blue_shift) |
 | 
			
		||||
          (a << spec.alpha_shift);
 | 
			
		||||
      }
 | 
			
		||||
      png_free(png, rows[y]);
 | 
			
		||||
    }
 | 
			
		||||
    png_free(png, rows);
 | 
			
		||||
 | 
			
		||||
    std::swap(*output_image, img);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  png_destroy_read_struct(&png, &info, nullptr);
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace x11
 | 
			
		||||
} // namespace clip
 | 
			
		||||
@@ -1,99 +0,0 @@
 | 
			
		||||
// Clip Library
 | 
			
		||||
// Copyright (c) 2015-2022 David Capello
 | 
			
		||||
//
 | 
			
		||||
// This file is released under the terms of the MIT license.
 | 
			
		||||
// Read LICENSE.txt for more information.
 | 
			
		||||
 | 
			
		||||
#include "clip.h"
 | 
			
		||||
 | 
			
		||||
namespace clip {
 | 
			
		||||
 | 
			
		||||
unsigned long image_spec::required_data_size() const
 | 
			
		||||
{
 | 
			
		||||
  unsigned long n = (bytes_per_row * height);
 | 
			
		||||
 | 
			
		||||
  // For 24bpp we add some extra space to access the last pixel (3
 | 
			
		||||
  // bytes) as an uint32_t
 | 
			
		||||
  if (bits_per_pixel == 24) {
 | 
			
		||||
    if ((n % 4) > 0)
 | 
			
		||||
      n += 4 - (n % 4);
 | 
			
		||||
    else
 | 
			
		||||
      ++n;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return n;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
image::image()
 | 
			
		||||
  : m_own_data(false),
 | 
			
		||||
    m_data(nullptr)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
image::image(const image_spec& spec)
 | 
			
		||||
  : m_own_data(true),
 | 
			
		||||
    m_data(new char[spec.required_data_size()]),
 | 
			
		||||
    m_spec(spec) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
image::image(const void* data, const image_spec& spec)
 | 
			
		||||
  : m_own_data(false),
 | 
			
		||||
    m_data((char*)data),
 | 
			
		||||
    m_spec(spec) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
image::image(const image& image)
 | 
			
		||||
  : m_own_data(false),
 | 
			
		||||
    m_data(nullptr),
 | 
			
		||||
    m_spec(image.m_spec) {
 | 
			
		||||
  copy_image(image);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
image::image(image&& image)
 | 
			
		||||
  : m_own_data(false),
 | 
			
		||||
    m_data(nullptr) {
 | 
			
		||||
  move_image(std::move(image));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
image::~image() {
 | 
			
		||||
  reset();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
image& image::operator=(const image& image) {
 | 
			
		||||
  copy_image(image);
 | 
			
		||||
  return *this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
image& image::operator=(image&& image) {
 | 
			
		||||
  move_image(std::move(image));
 | 
			
		||||
  return *this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void image::reset() {
 | 
			
		||||
  if (m_own_data) {
 | 
			
		||||
    delete[] m_data;
 | 
			
		||||
    m_own_data = false;
 | 
			
		||||
    m_data = nullptr;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void image::copy_image(const image& image) {
 | 
			
		||||
  reset();
 | 
			
		||||
 | 
			
		||||
  m_spec = image.spec();
 | 
			
		||||
  std::size_t n = m_spec.required_data_size();
 | 
			
		||||
 | 
			
		||||
  m_own_data = true;
 | 
			
		||||
  m_data = new char[n];
 | 
			
		||||
  std::copy(image.data(),
 | 
			
		||||
            image.data()+n,
 | 
			
		||||
            m_data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void image::move_image(image&& image) {
 | 
			
		||||
  std::swap(m_own_data, image.m_own_data);
 | 
			
		||||
  std::swap(m_data, image.m_data);
 | 
			
		||||
  std::swap(m_spec, image.m_spec);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace clip
 | 
			
		||||
@@ -2,7 +2,6 @@
 | 
			
		||||
#include <regex>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <fstream>
 | 
			
		||||
#include "clip-lib/clip.h"
 | 
			
		||||
#include <random>
 | 
			
		||||
using namespace std;
 | 
			
		||||
 | 
			
		||||
@@ -98,7 +97,6 @@ int help() {
 | 
			
		||||
    cout << "  -h  Show this help message" << endl;
 | 
			
		||||
    cout << "  -f  Read from file" << endl;
 | 
			
		||||
    cout << "  -o  Write to file" << endl;
 | 
			
		||||
    cout << "  -c  Copy to clipboard" << endl;
 | 
			
		||||
    cout << "  -ne Stops extra stuff from being added to the end of sentences" << endl;
 | 
			
		||||
    cout << "  -nf Stops full word replacements" << endl;
 | 
			
		||||
    cout << "  text  Text to convert" << endl;
 | 
			
		||||
@@ -129,8 +127,6 @@ int main(int argc, char *argv[]) {
 | 
			
		||||
            return help();
 | 
			
		||||
        } else if (i == argc-1) {
 | 
			
		||||
            input = string(argv[i]);
 | 
			
		||||
        } else if (string(argv[i]) == "-c") {
 | 
			
		||||
            clipboard = true;
 | 
			
		||||
        } else if (string(argv[i]) == "-ne") {
 | 
			
		||||
            no_random_ending = true;
 | 
			
		||||
        } else if (string(argv[i]) == "-nf") {
 | 
			
		||||
@@ -189,9 +185,6 @@ int main(int argc, char *argv[]) {
 | 
			
		||||
    } else if (input != "") {
 | 
			
		||||
        if (outputfile != "") {
 | 
			
		||||
            *output << owo(input, no_full_word, no_random_ending) << endl;
 | 
			
		||||
        } else if (clipboard) {
 | 
			
		||||
            clip::set_text(owo(input, no_full_word, no_random_ending));
 | 
			
		||||
            cout << owo("Copied to clipboard") << endl;
 | 
			
		||||
        } else {
 | 
			
		||||
            cout << owo(input, no_full_word, no_random_ending) << endl;
 | 
			
		||||
        }
 | 
			
		||||
@@ -201,6 +194,5 @@ int main(int argc, char *argv[]) {
 | 
			
		||||
        ((ofstream*)output)->close();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user