1 // Aseprite
2 // Copyright (C) 2016-2018 David Capello
3 //
4 // This program is distributed under the terms of
5 // the End-User License Agreement for Aseprite.
6
7 #ifdef HAVE_CONFIG_H
8 #include "config.h"
9 #endif
10
11 #include "app/util/clipboard_native.h"
12
13 #include "app/i18n/strings.h"
14 #include "base/serialization.h"
15 #include "base/unique_ptr.h"
16 #include "clip/clip.h"
17 #include "doc/color_scales.h"
18 #include "doc/image.h"
19 #include "doc/image_impl.h"
20 #include "doc/image_io.h"
21 #include "doc/mask_io.h"
22 #include "doc/palette_io.h"
23 #include "gfx/size.h"
24 #include "she/display.h"
25 #include "she/system.h"
26 #include "ui/alert.h"
27
28 #include <sstream>
29 #include <vector>
30
31 namespace app {
32 namespace clipboard {
33
34 using namespace base::serialization;
35 using namespace base::serialization::little_endian;
36
37 namespace {
38 clip::format custom_image_format = 0;
39
native_display_handle()40 void* native_display_handle() {
41 return she::instance()->defaultDisplay()->nativeHandle();
42 }
43
custom_error_handler(clip::ErrorCode code)44 void custom_error_handler(clip::ErrorCode code) {
45 switch (code) {
46 case clip::ErrorCode::CannotLock:
47 ui::Alert::show(Strings::alerts_clipboard_access_locked());
48 break;
49 case clip::ErrorCode::ImageNotSupported:
50 ui::Alert::show(Strings::alerts_clipboard_image_format_not_supported());
51 break;
52 }
53 }
54
55 }
56
register_native_clipboard_formats()57 void register_native_clipboard_formats()
58 {
59 clip::set_error_handler(custom_error_handler);
60 custom_image_format = clip::register_format("org.aseprite.Image");
61 }
62
has_native_clipboard_bitmap()63 bool has_native_clipboard_bitmap()
64 {
65 return clip::has(clip::image_format());
66 }
67
set_native_clipboard_bitmap(const doc::Image * image,const doc::Mask * mask,const doc::Palette * palette)68 bool set_native_clipboard_bitmap(const doc::Image* image,
69 const doc::Mask* mask,
70 const doc::Palette* palette)
71 {
72 clip::lock l(native_display_handle());
73 if (!l.locked())
74 return false;
75
76 l.clear();
77
78 if (!image)
79 return false;
80
81 // Set custom clipboard formats
82 if (custom_image_format) {
83 std::stringstream os;
84 write32(os,
85 (image ? 1: 0) |
86 (mask ? 2: 0) |
87 (palette ? 4: 0));
88 if (image) doc::write_image(os, image);
89 if (mask) doc::write_mask(os, mask);
90 if (palette) doc::write_palette(os, palette);
91
92 if (os.good()) {
93 size_t size = (size_t)os.tellp();
94 if (size > 0) {
95 std::vector<char> data(size);
96 os.seekp(0);
97 os.read(&data[0], size);
98
99 l.set_data(custom_image_format, &data[0], size);
100 }
101 }
102 }
103
104 clip::image_spec spec;
105 spec.width = image->width();
106 spec.height = image->height();
107 spec.bits_per_pixel = 32;
108 spec.bytes_per_row = (image->pixelFormat() == doc::IMAGE_RGB ?
109 image->getRowStrideSize(): 4*spec.width);
110 spec.red_mask = doc::rgba_r_mask;
111 spec.green_mask = doc::rgba_g_mask;
112 spec.blue_mask = doc::rgba_b_mask;
113 spec.alpha_mask = doc::rgba_a_mask;
114 spec.red_shift = doc::rgba_r_shift;
115 spec.green_shift = doc::rgba_g_shift;
116 spec.blue_shift = doc::rgba_b_shift;
117 spec.alpha_shift = doc::rgba_a_shift;
118
119 switch (image->pixelFormat()) {
120 case doc::IMAGE_RGB: {
121 // We use the RGB image data directly
122 clip::image img(image->getPixelAddress(0, 0), spec);
123 l.set_image(img);
124 break;
125 }
126 case doc::IMAGE_GRAYSCALE: {
127 clip::image img(spec);
128 const doc::LockImageBits<doc::GrayscaleTraits> bits(image);
129 auto it = bits.begin();
130 uint32_t* dst = (uint32_t*)img.data();
131 for (int y=0; y<image->height(); ++y) {
132 for (int x=0; x<image->width(); ++x, ++it) {
133 doc::color_t c = *it;
134 *(dst++) = doc::rgba(doc::graya_getv(c),
135 doc::graya_getv(c),
136 doc::graya_getv(c),
137 doc::graya_geta(c));
138 }
139 }
140 l.set_image(img);
141 break;
142 }
143 case doc::IMAGE_INDEXED: {
144 clip::image img(spec);
145 const doc::LockImageBits<doc::IndexedTraits> bits(image);
146 auto it = bits.begin();
147 uint32_t* dst = (uint32_t*)img.data();
148 for (int y=0; y<image->height(); ++y) {
149 for (int x=0; x<image->width(); ++x, ++it) {
150 doc::color_t c = palette->getEntry(*it);
151
152 // Use alpha=0 for mask color
153 if (*it == image->maskColor())
154 c &= doc::rgba_rgb_mask;
155
156 *(dst++) = c;
157 }
158 }
159 l.set_image(img);
160 break;
161 }
162 }
163
164 return true;
165 }
166
get_native_clipboard_bitmap(doc::Image ** image,doc::Mask ** mask,doc::Palette ** palette)167 bool get_native_clipboard_bitmap(doc::Image** image,
168 doc::Mask** mask,
169 doc::Palette** palette)
170 {
171 *image = nullptr;
172 *mask = nullptr;
173 *palette = nullptr;
174
175 clip::lock l(native_display_handle());
176 if (!l.locked())
177 return false;
178
179 // Prefer the custom format (to avoid losing mask and palette)
180 if (l.is_convertible(custom_image_format)) {
181 size_t size = l.get_data_length(custom_image_format);
182 if (size > 0) {
183 std::vector<char> buf(size);
184 if (l.get_data(custom_image_format, &buf[0], size)) {
185 std::stringstream is;
186 is.write(&buf[0], size);
187 is.seekp(0);
188
189 int bits = read32(is);
190 if (bits & 1) *image = doc::read_image(is, false);
191 if (bits & 2) *mask = doc::read_mask(is);
192 if (bits & 4) *palette = doc::read_palette(is);
193 if (image)
194 return true;
195 }
196 }
197 }
198
199 if (!l.is_convertible(clip::image_format()))
200 return false;
201
202 clip::image img;
203 if (!l.get_image(img))
204 return false;
205
206 const clip::image_spec& spec = img.spec();
207
208 base::UniquePtr<doc::Image> dst(
209 doc::Image::create(doc::IMAGE_RGB,
210 spec.width, spec.height));
211
212 switch (spec.bits_per_pixel) {
213 case 64: {
214 doc::LockImageBits<doc::RgbTraits> bits(dst.get(), doc::Image::WriteLock);
215 auto it = bits.begin();
216 for (unsigned long y=0; y<spec.height; ++y) {
217 const uint64_t* src = (const uint64_t*)(img.data()+spec.bytes_per_row*y);
218 for (unsigned long x=0; x<spec.width; ++x, ++it, ++src) {
219 uint64_t c = *((const uint64_t*)src);
220 *it = doc::rgba(
221 uint8_t((c & spec.red_mask) >> spec.red_shift >> 8),
222 uint8_t((c & spec.green_mask) >> spec.green_shift >> 8),
223 uint8_t((c & spec.blue_mask) >> spec.blue_shift >> 8),
224 uint8_t((c & spec.alpha_mask) >> spec.alpha_shift >> 8));
225 }
226 }
227 break;
228 }
229 case 32: {
230 doc::LockImageBits<doc::RgbTraits> bits(dst.get(), doc::Image::WriteLock);
231 auto it = bits.begin();
232 for (unsigned long y=0; y<spec.height; ++y) {
233 const uint32_t* src = (const uint32_t*)(img.data()+spec.bytes_per_row*y);
234 for (unsigned long x=0; x<spec.width; ++x, ++it, ++src) {
235 const uint32_t c = *((const uint32_t*)src);
236
237 // The alpha mask can be zero (which means that the image is
238 // just RGB).
239 int alpha =
240 (spec.alpha_mask ?
241 uint8_t((c & spec.alpha_mask) >> spec.alpha_shift): 255);
242
243 *it = doc::rgba(
244 uint8_t((c & spec.red_mask ) >> spec.red_shift ),
245 uint8_t((c & spec.green_mask) >> spec.green_shift),
246 uint8_t((c & spec.blue_mask ) >> spec.blue_shift ),
247 alpha);
248 }
249 }
250 break;
251 }
252 case 24: {
253 doc::LockImageBits<doc::RgbTraits> bits(dst.get(), doc::Image::WriteLock);
254 auto it = bits.begin();
255 for (unsigned long y=0; y<spec.height; ++y) {
256 const char* src = (const char*)(img.data()+spec.bytes_per_row*y);
257 for (unsigned long x=0; x<spec.width; ++x, ++it, src+=3) {
258 unsigned long c = *((const unsigned long*)src);
259 *it = doc::rgba(
260 uint8_t((c & spec.red_mask) >> spec.red_shift),
261 uint8_t((c & spec.green_mask) >> spec.green_shift),
262 uint8_t((c & spec.blue_mask) >> spec.blue_shift),
263 255);
264 }
265 }
266 break;
267 }
268 case 16: {
269 doc::LockImageBits<doc::RgbTraits> bits(dst.get(), doc::Image::WriteLock);
270 auto it = bits.begin();
271 for (unsigned long y=0; y<spec.height; ++y) {
272 const uint16_t* src = (const uint16_t*)(img.data()+spec.bytes_per_row*y);
273 for (unsigned long x=0; x<spec.width; ++x, ++it, ++src) {
274 const uint16_t c = *((const uint16_t*)src);
275 *it = doc::rgba(
276 doc::scale_5bits_to_8bits((c & spec.red_mask ) >> spec.red_shift ),
277 doc::scale_6bits_to_8bits((c & spec.green_mask) >> spec.green_shift),
278 doc::scale_5bits_to_8bits((c & spec.blue_mask ) >> spec.blue_shift ),
279 255);
280 }
281 }
282 break;
283 }
284 }
285
286 *image = dst.release();
287 return true;
288 }
289
get_native_clipboard_bitmap_size(gfx::Size * size)290 bool get_native_clipboard_bitmap_size(gfx::Size* size)
291 {
292 clip::image_spec spec;
293 if (clip::get_image_spec(spec)) {
294 size->w = spec.width;
295 size->h = spec.height;
296 return true;
297 }
298 else
299 return false;
300 }
301
302 } // namespace clipboard
303 } // namespace app
304