layer modes: add soft light blend mode
[mypaint:mypaint.git] / lib / pixops.hpp
1 /* This file is part of MyPaint.
2  * Copyright (C) 2008-2009 by Martin Renold <martinxyz@gmx.ch>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  */
9
10 // make the "heavy_debug" readable from python
11 #ifdef HEAVY_DEBUG
12 const bool heavy_debug = true;
13 #else
14 const bool heavy_debug = false;
15 #endif
16
17 // downscale a tile to half its size using bilinear interpolation
18 // used for generating mipmaps for tiledsurface and background
19 void tile_downscale_rgba16(PyObject *src, PyObject *dst, int dst_x, int dst_y) {
20 #ifdef HEAVY_DEBUG
21   assert(PyArray_DIM(src, 0) == TILE_SIZE);
22   assert(PyArray_DIM(src, 1) == TILE_SIZE);
23   assert(PyArray_DIM(src, 2) == 4);
24   assert(PyArray_TYPE(src) == NPY_UINT16);
25   assert(PyArray_ISCARRAY(src));
26
27   assert(PyArray_DIM(dst, 2) == 4);
28   assert(PyArray_TYPE(dst) == NPY_UINT16);
29   assert(PyArray_ISCARRAY(dst));
30 #endif
31
32   PyArrayObject* src_arr = ((PyArrayObject*)src);
33   PyArrayObject* dst_arr = ((PyArrayObject*)dst);
34
35   for (int y=0; y<TILE_SIZE/2; y++) {
36     uint16_t * src_p = (uint16_t*)(src_arr->data + (2*y)*src_arr->strides[0]);
37     uint16_t * dst_p = (uint16_t*)(dst_arr->data + (y+dst_y)*dst_arr->strides[0]);
38     dst_p += 4*dst_x;
39     for(int x=0; x<TILE_SIZE/2; x++) {
40       dst_p[0] = src_p[0]/4 + (src_p+4)[0]/4 + (src_p+4*TILE_SIZE)[0]/4 + (src_p+4*TILE_SIZE+4)[0]/4;
41       dst_p[1] = src_p[1]/4 + (src_p+4)[1]/4 + (src_p+4*TILE_SIZE)[1]/4 + (src_p+4*TILE_SIZE+4)[1]/4;
42       dst_p[2] = src_p[2]/4 + (src_p+4)[2]/4 + (src_p+4*TILE_SIZE)[2]/4 + (src_p+4*TILE_SIZE+4)[2]/4;
43       dst_p[3] = src_p[3]/4 + (src_p+4)[3]/4 + (src_p+4*TILE_SIZE)[3]/4 + (src_p+4*TILE_SIZE+4)[3]/4;
44       src_p += 8;
45       dst_p += 4;
46     }
47   }
48 }
49
50
51 #include "composite.hpp"
52
53 #define _COMPOSITE_TILES(src, dst, dst_has_alpha, opac, opname)     \
54   uint16_t *src_p  = (uint16_t*)((PyArrayObject*)src)->data;        \
55   char *p = dst_arr->data;                                          \
56   if (dst_has_alpha) {                                              \
57     for (int y=0; y<TILE_SIZE; y++) {                               \
58       uint16_t *dst_p  = (uint16_t*) (p);                           \
59       for (int x=0; x<TILE_SIZE; x++) {                             \
60         rgba_composite_ ## opname ## _rgba(src_p, dst_p, opac);     \
61         src_p += 4;                                                 \
62         dst_p += 4;                                                 \
63       }                                     \
64       p += dst_arr->strides[0];             \
65     }                                       \
66   }                                         \
67   else {                                                            \
68     for (int y=0; y<TILE_SIZE; y++) {                               \
69       uint16_t *dst_p  = (uint16_t*) (p);                           \
70       for (int x=0; x<TILE_SIZE; x++) {                             \
71         rgba_composite_ ## opname ## _rgbu(src_p, dst_p, opac);     \
72         src_p += 4;                                                 \
73         dst_p += 4;                                                 \
74       }                                     \
75       p += dst_arr->strides[0];             \
76     }                                       \
77   }                                         //_COMPOSITE_TILES loop macro
78
79
80
81 /**
82  * tile_composite_src_over:
83  *
84  * @src: upper source tile, unmodified
85  * @dst: lower destination tile, will be modified
86  * @dst_has_alpha: true if @dst's alpha should be processed
87  * @src_opacity: overall multiplier for @src's alpha
88  *
89  * The default layer compositing operation. Composites two tiles using the
90  * usual Porter-Duff OVER operator, src OVER dst.
91  *
92  * Dimensions of both arrays must be (TILE_SIZE, TILE_SIZE, 4). If
93  * @dst_has_alpha is false, @dst's alpha is ignored and treated as 100%, which
94  * results in faster operation and generates opaque output.
95  */
96
97 void
98 tile_composite_src_over (PyObject *src,
99                          PyObject *dst,
100                          const bool dst_has_alpha,
101                          const float src_opacity)
102 {
103 #ifdef HEAVY_DEBUG
104   assert(PyArray_DIM(src, 0) == TILE_SIZE);
105   assert(PyArray_DIM(src, 1) == TILE_SIZE);
106   assert(PyArray_DIM(src, 2) == 4);
107   assert(PyArray_TYPE(src) == NPY_UINT16);
108   assert(PyArray_ISCARRAY(src));
109
110   assert(PyArray_DIM(dst, 0) == TILE_SIZE);
111   assert(PyArray_DIM(dst, 1) == TILE_SIZE);
112   assert(PyArray_DIM(dst, 2) == 4);
113   assert(PyArray_TYPE(dst) == NPY_UINT16);
114   assert(PyArray_ISBEHAVED(dst));
115 #endif
116
117   PyArrayObject* dst_arr = ((PyArrayObject*)dst);
118 #ifdef HEAVY_DEBUG
119   assert(dst_arr->strides[1] == 4*sizeof(uint16_t));
120   assert(dst_arr->strides[2] ==   sizeof(uint16_t));
121 #endif
122
123   // The overall layer opacity is applied as a scaling factor to the src
124   // layer's alpha, and to the premultiplied src colour components. In the
125   // derivations below,
126   //
127   // Sa = opac * src_p[3]       ; src's effective alpha
128   // Sca = opac * src_p[c]      ; effective src component incl. premult alpha
129
130   const uint32_t opac = CLAMP(src_opacity * (1<<15) + 0.5, 0, (1<<15));
131   if (opac == 0) return;
132
133   _COMPOSITE_TILES(src, dst, dst_has_alpha, opac, src_over);
134 }
135
136
137 /**
138  * tile_composite_multiply:
139  *
140  * @src: upper source tile, unmodified
141  * @dst: lower destination tile, will be modified
142  * @dst_has_alpha: true if @dst's alpha should be processed
143  * @src_opacity: overall multiplier for @src's alpha
144  *
145  * Multiplies together each pixel in @src and @dst, writing the result into
146  * @dst. The result is always at least as dark as either of the input tiles.
147  *
148  * Dimensions of both arrays must be (TILE_SIZE, TILE_SIZE, 4). If
149  * @dst_has_alpha is false, @dst's alpha is ignored and treated as 100%, which
150  * results in faster operation and generates opaque output.
151  */
152
153 void
154 tile_composite_multiply (PyObject *src,
155                          PyObject *dst,
156                          const bool dst_has_alpha,
157                          const float src_opacity)
158 {
159 #ifdef HEAVY_DEBUG
160   assert(PyArray_DIM(src, 0) == TILE_SIZE);
161   assert(PyArray_DIM(src, 1) == TILE_SIZE);
162   assert(PyArray_DIM(src, 2) == 4);
163   assert(PyArray_TYPE(src) == NPY_UINT16);
164   assert(PyArray_ISCARRAY(src));
165
166   assert(PyArray_DIM(dst, 0) == TILE_SIZE);
167   assert(PyArray_DIM(dst, 1) == TILE_SIZE);
168   assert(PyArray_DIM(dst, 2) == 4);
169   assert(PyArray_TYPE(dst) == NPY_UINT16);
170   assert(PyArray_ISBEHAVED(dst));
171 #endif
172
173   PyArrayObject* dst_arr = ((PyArrayObject*)dst);
174 #ifdef HEAVY_DEBUG
175   assert(dst_arr->strides[1] == 4*sizeof(uint16_t));
176   assert(dst_arr->strides[2] ==   sizeof(uint16_t));
177 #endif
178
179   const uint32_t opac = CLAMP(src_opacity * (1<<15) + 0.5, 0, (1<<15));
180   if (opac == 0) return;
181
182   _COMPOSITE_TILES(src, dst, dst_has_alpha, opac, multiply);
183 }
184
185
186 /**
187  * tile_composite_screen:
188  *
189  * @src: upper source tile, unmodified
190  * @dst: lower destination tile, will be modified
191  * @dst_has_alpha: true if @dst's alpha should be processed
192  * @src_opacity: overall multiplier for @src's alpha
193  *
194  * Multiplies together the complements of each pixel in @src and @dst, writing
195  * the result into @dst. The result is always lighter than either of the input
196  * tiles.
197  *
198  * Dimensions of both arrays must be (TILE_SIZE, TILE_SIZE, 4). If
199  * @dst_has_alpha is false, @dst's alpha is ignored and treated as 100%, which
200  * results in faster operation and generates opaque output.
201  */
202
203 void
204 tile_composite_screen (PyObject *src,
205                        PyObject *dst,
206                        const bool dst_has_alpha,
207                        const float src_opacity)
208 {
209 #ifdef HEAVY_DEBUG
210   assert(PyArray_DIM(src, 0) == TILE_SIZE);
211   assert(PyArray_DIM(src, 1) == TILE_SIZE);
212   assert(PyArray_DIM(src, 2) == 4);
213   assert(PyArray_TYPE(src) == NPY_UINT16);
214   assert(PyArray_ISCARRAY(src));
215
216   assert(PyArray_DIM(dst, 0) == TILE_SIZE);
217   assert(PyArray_DIM(dst, 1) == TILE_SIZE);
218   assert(PyArray_DIM(dst, 2) == 4);
219   assert(PyArray_TYPE(dst) == NPY_UINT16);
220   assert(PyArray_ISBEHAVED(dst));
221 #endif
222   PyArrayObject* dst_arr = ((PyArrayObject*)dst);
223 #ifdef HEAVY_DEBUG
224   assert(dst_arr->strides[1] == 4*sizeof(uint16_t));
225   assert(dst_arr->strides[2] ==   sizeof(uint16_t));
226 #endif
227
228   const uint32_t opac = CLAMP(src_opacity * (1<<15) + 0.5, 0, (1<<15));
229   if (opac == 0) return;
230
231   _COMPOSITE_TILES(src, dst, dst_has_alpha, opac, screen);
232 }
233
234
235 /**
236  * tile_composite_overlay:
237  *
238  * @src: upper source tile, unmodified
239  * @dst: lower destination tile, will be modified
240  * @dst_has_alpha: true if @dst's alpha should be processed
241  * @src_opacity: overall multiplier for @src's alpha
242  *
243  * The destination color component determines if this is a multiply or
244  * a screen operation.
245  *
246  * Dimensions of both arrays must be (TILE_SIZE, TILE_SIZE, 4). If
247  * @dst_has_alpha is false, @dst's alpha is ignored and treated as 100%, which
248  * results in faster operation and generates opaque output.
249  */
250
251 void
252 tile_composite_overlay (PyObject *src,
253                         PyObject *dst,
254                         const bool dst_has_alpha,
255                         const float src_opacity)
256 {
257 #ifdef HEAVY_DEBUG
258   assert(PyArray_DIM(src, 0) == TILE_SIZE);
259   assert(PyArray_DIM(src, 1) == TILE_SIZE);
260   assert(PyArray_DIM(src, 2) == 4);
261   assert(PyArray_TYPE(src) == NPY_UINT16);
262   assert(PyArray_ISCARRAY(src));
263
264   assert(PyArray_DIM(dst, 0) == TILE_SIZE);
265   assert(PyArray_DIM(dst, 1) == TILE_SIZE);
266   assert(PyArray_DIM(dst, 2) == 4);
267   assert(PyArray_TYPE(dst) == NPY_UINT16);
268   assert(PyArray_ISBEHAVED(dst));
269 #endif
270   PyArrayObject* dst_arr = ((PyArrayObject*)dst);
271 #ifdef HEAVY_DEBUG
272   assert(dst_arr->strides[1] == 4*sizeof(uint16_t));
273   assert(dst_arr->strides[2] ==   sizeof(uint16_t));
274 #endif
275
276   const uint32_t opac = CLAMP(src_opacity * (1<<15) + 0.5, 0, (1<<15));
277   if (opac == 0) return;
278
279   _COMPOSITE_TILES(src, dst, dst_has_alpha, opac, overlay);
280 }
281
282
283 /**
284  * tile_composite_hard_light:
285  *
286  * @src: upper source tile, unmodified
287  * @dst: lower destination tile, will be modified
288  * @dst_has_alpha: true if @dst's alpha should be processed
289  * @src_opacity: overall multiplier for @src's alpha
290  *
291  * The source color component determines if this is a multiply or a screen
292  * operation. The effect is similar to a harsh spotlight being shone on the
293  * destination.
294  *
295  * Dimensions of both arrays must be (TILE_SIZE, TILE_SIZE, 4). If
296  * @dst_has_alpha is false, @dst's alpha is ignored and treated as 100%, which
297  * results in faster operation and generates opaque output.
298  */
299
300 void
301 tile_composite_hard_light (PyObject *src,
302                            PyObject *dst,
303                            const bool dst_has_alpha,
304                            const float src_opacity)
305 {
306 #ifdef HEAVY_DEBUG
307   assert(PyArray_DIM(src, 0) == TILE_SIZE);
308   assert(PyArray_DIM(src, 1) == TILE_SIZE);
309   assert(PyArray_DIM(src, 2) == 4);
310   assert(PyArray_TYPE(src) == NPY_UINT16);
311   assert(PyArray_ISCARRAY(src));
312
313   assert(PyArray_DIM(dst, 0) == TILE_SIZE);
314   assert(PyArray_DIM(dst, 1) == TILE_SIZE);
315   assert(PyArray_DIM(dst, 2) == 4);
316   assert(PyArray_TYPE(dst) == NPY_UINT16);
317   assert(PyArray_ISBEHAVED(dst));
318 #endif
319   PyArrayObject* dst_arr = ((PyArrayObject*)dst);
320 #ifdef HEAVY_DEBUG
321   assert(dst_arr->strides[1] == 4*sizeof(uint16_t));
322   assert(dst_arr->strides[2] ==   sizeof(uint16_t));
323 #endif
324
325   const uint32_t opac = CLAMP(src_opacity * (1<<15) + 0.5, 0, (1<<15));
326   if (opac == 0) return;
327
328   _COMPOSITE_TILES(src, dst, dst_has_alpha, opac, hard_light);
329 }
330
331
332 /**
333  * tile_composite_soft_light:
334  *
335  * @src: upper source tile, unmodified
336  * @dst: lower destination tile, will be modified
337  * @dst_has_alpha: true if @dst's alpha should be processed
338  * @src_opacity: overall multiplier for @src's alpha
339  *
340  * The source color component determines whether the destination is lightened
341  * or darkened. The effect is similar to shining a diffused spotlight on dst.
342  *
343  * Dimensions of both arrays must be (TILE_SIZE, TILE_SIZE, 4). If
344  * @dst_has_alpha is false, @dst's alpha is ignored and treated as 100%, which
345  * results in faster operation and generates opaque output.
346  */
347
348 void
349 tile_composite_soft_light (PyObject *src,
350                            PyObject *dst,
351                            const bool dst_has_alpha,
352                            const float src_opacity)
353 {
354 #ifdef HEAVY_DEBUG
355   assert(PyArray_DIM(src, 0) == TILE_SIZE);
356   assert(PyArray_DIM(src, 1) == TILE_SIZE);
357   assert(PyArray_DIM(src, 2) == 4);
358   assert(PyArray_TYPE(src) == NPY_UINT16);
359   assert(PyArray_ISCARRAY(src));
360
361   assert(PyArray_DIM(dst, 0) == TILE_SIZE);
362   assert(PyArray_DIM(dst, 1) == TILE_SIZE);
363   assert(PyArray_DIM(dst, 2) == 4);
364   assert(PyArray_TYPE(dst) == NPY_UINT16);
365   assert(PyArray_ISBEHAVED(dst));
366 #endif
367   PyArrayObject* dst_arr = ((PyArrayObject*)dst);
368 #ifdef HEAVY_DEBUG
369   assert(dst_arr->strides[1] == 4*sizeof(uint16_t));
370   assert(dst_arr->strides[2] ==   sizeof(uint16_t));
371 #endif
372
373   const uint32_t opac = CLAMP(src_opacity * (1<<15) + 0.5, 0, (1<<15));
374   if (opac == 0) return;
375
376   _COMPOSITE_TILES(src, dst, dst_has_alpha, opac, soft_light);
377 }
378
379
380
381
382 /**
383  * tile_composite_color_dodge:
384  *
385  * @src: upper source tile, unmodified
386  * @dst: lower destination tile, will be modified
387  * @dst_has_alpha: true if @dst's alpha should be processed
388  * @src_opacity: overall multiplier for @src's alpha
389  *
390  * Brightens @dst to reflect @src. Using black in @src preserves the colour in
391  * @dst.
392  *
393  * Dimensions of both arrays must be (TILE_SIZE, TILE_SIZE, 4). If
394  * @dst_has_alpha is false, @dst's alpha is ignored and treated as 100%, which
395  * results in faster operation and generates opaque output.
396  */
397
398 void
399 tile_composite_color_dodge (PyObject *src,
400                             PyObject *dst,
401                             const bool dst_has_alpha,
402                             const float src_opacity)
403 {
404 #ifdef HEAVY_DEBUG
405   assert(PyArray_DIM(src, 0) == TILE_SIZE);
406   assert(PyArray_DIM(src, 1) == TILE_SIZE);
407   assert(PyArray_DIM(src, 2) == 4);
408   assert(PyArray_TYPE(src) == NPY_UINT16);
409   assert(PyArray_ISCARRAY(src));
410
411   assert(PyArray_DIM(dst, 0) == TILE_SIZE);
412   assert(PyArray_DIM(dst, 1) == TILE_SIZE);
413   assert(PyArray_DIM(dst, 2) == 4);
414   assert(PyArray_TYPE(dst) == NPY_UINT16);
415   assert(PyArray_ISBEHAVED(dst));
416 #endif
417
418   PyArrayObject* dst_arr = ((PyArrayObject*)dst);
419 #ifdef HEAVY_DEBUG
420   assert(dst_arr->strides[1] == 4*sizeof(uint16_t));
421   assert(dst_arr->strides[2] ==   sizeof(uint16_t));
422 #endif
423
424   const uint32_t opac = CLAMP(src_opacity * (1<<15) + 0.5, 0, (1<<15));
425   if (opac == 0) return;
426
427   _COMPOSITE_TILES(src, dst, dst_has_alpha, opac, color_dodge);
428 }
429
430
431
432 /**
433  * tile_composite_color_burn:
434  *
435  * @src: upper source tile, unmodified
436  * @dst: lower destination tile, will be modified
437  * @dst_has_alpha: true if @dst's alpha should be processed
438  * @src_opacity: overall multiplier for @src's alpha
439  *
440  * Darkens @dst to reflect @src. Using white in @src preserves the colour in
441  * @dst.
442  *
443  * Dimensions of both arrays must be (TILE_SIZE, TILE_SIZE, 4). If
444  * @dst_has_alpha is false, @dst's alpha is ignored and treated as 100%, which
445  * results in faster operation and generates opaque output.
446  */
447
448 void
449 tile_composite_color_burn (PyObject *src,
450                            PyObject *dst,
451                            const bool dst_has_alpha,
452                            const float src_opacity)
453 {
454 #ifdef HEAVY_DEBUG
455   assert(PyArray_DIM(src, 0) == TILE_SIZE);
456   assert(PyArray_DIM(src, 1) == TILE_SIZE);
457   assert(PyArray_DIM(src, 2) == 4);
458   assert(PyArray_TYPE(src) == NPY_UINT16);
459   assert(PyArray_ISCARRAY(src));
460
461   assert(PyArray_DIM(dst, 0) == TILE_SIZE);
462   assert(PyArray_DIM(dst, 1) == TILE_SIZE);
463   assert(PyArray_DIM(dst, 2) == 4);
464   assert(PyArray_TYPE(dst) == NPY_UINT16);
465   assert(PyArray_ISBEHAVED(dst));
466 #endif
467
468   PyArrayObject* dst_arr = ((PyArrayObject*)dst);
469 #ifdef HEAVY_DEBUG
470   assert(dst_arr->strides[1] == 4*sizeof(uint16_t));
471   assert(dst_arr->strides[2] ==   sizeof(uint16_t));
472 #endif
473
474   const uint32_t opac = CLAMP(src_opacity * (1<<15) + 0.5, 0, (1<<15));
475   if (opac == 0) return;
476
477   _COMPOSITE_TILES(src, dst, dst_has_alpha, opac, color_burn);
478 }
479
480
481
482 /**
483  * tile_composite_color
484  *
485  * @src: upper source tile, unmodified
486  * @dst: lower destination tile, will be modified
487  * @dst_has_alpha: true if @dst's alpha should be processed
488  * @src_opacity: overall multiplier for @src's alpha
489  *
490  * Colorizes @dst with hues and saturations from @src. Colourizing with
491  * pure grey has no effect.
492  *
493  * Dimensions of both arrays must be (TILE_SIZE, TILE_SIZE, 4). If
494  * @dst_has_alpha is false, @dst's alpha is ignored and treated as 100%, which
495  * results in faster operation and generates opaque output.
496  */
497
498 void
499 tile_composite_color (PyObject *src,
500                       PyObject *dst,
501                       const bool dst_has_alpha,
502                       const float src_opacity)
503 {
504 #ifdef HEAVY_DEBUG
505   assert(PyArray_DIM(src, 0) == TILE_SIZE);
506   assert(PyArray_DIM(src, 1) == TILE_SIZE);
507   assert(PyArray_DIM(src, 2) == 4);
508   assert(PyArray_TYPE(src) == NPY_UINT16);
509   assert(PyArray_ISCARRAY(src));
510
511   assert(PyArray_DIM(dst, 0) == TILE_SIZE);
512   assert(PyArray_DIM(dst, 1) == TILE_SIZE);
513   assert(PyArray_DIM(dst, 2) == 4);
514   assert(PyArray_TYPE(dst) == NPY_UINT16);
515   assert(PyArray_ISBEHAVED(dst));
516 #endif
517
518   PyArrayObject* dst_arr = ((PyArrayObject*)dst);
519 #ifdef HEAVY_DEBUG
520   assert(dst_arr->strides[1] == 4*sizeof(uint16_t));
521   assert(dst_arr->strides[2] ==   sizeof(uint16_t));
522 #endif
523
524   const uint32_t opac = CLAMP(src_opacity * (1<<15) + 0.5, 0, (1<<15));
525   if (opac == 0) return;
526
527   _COMPOSITE_TILES(src, dst, dst_has_alpha, opac, color);
528 }
529
530
531 /**
532  * tile_composite_luminosity
533  *
534  * @src: upper source tile, unmodified
535  * @dst: lower destination tile, will be modified
536  * @dst_has_alpha: true if @dst's alpha should be processed
537  * @src_opacity: overall multiplier for @src's alpha
538  *
539  * Sets the lightness of @dst to that of the pixels in @src. This is the
540  * exact inverse of tile_composite_color().
541  *
542  * Dimensions of both arrays must be (TILE_SIZE, TILE_SIZE, 4). If
543  * @dst_has_alpha is false, @dst's alpha is ignored and treated as 100%, which
544  * results in faster operation and generates opaque output.
545  */
546
547 void
548 tile_composite_luminosity (PyObject *src,
549                            PyObject *dst,
550                            const bool dst_has_alpha,
551                            const float src_opacity)
552 {
553 #ifdef HEAVY_DEBUG
554   assert(PyArray_DIM(src, 0) == TILE_SIZE);
555   assert(PyArray_DIM(src, 1) == TILE_SIZE);
556   assert(PyArray_DIM(src, 2) == 4);
557   assert(PyArray_TYPE(src) == NPY_UINT16);
558   assert(PyArray_ISCARRAY(src));
559
560   assert(PyArray_DIM(dst, 0) == TILE_SIZE);
561   assert(PyArray_DIM(dst, 1) == TILE_SIZE);
562   assert(PyArray_DIM(dst, 2) == 4);
563   assert(PyArray_TYPE(dst) == NPY_UINT16);
564   assert(PyArray_ISBEHAVED(dst));
565 #endif
566
567   PyArrayObject* dst_arr = ((PyArrayObject*)dst);
568 #ifdef HEAVY_DEBUG
569   assert(dst_arr->strides[1] == 4*sizeof(uint16_t));
570   assert(dst_arr->strides[2] ==   sizeof(uint16_t));
571 #endif
572
573   const uint32_t opac = CLAMP(src_opacity * (1<<15) + 0.5, 0, (1<<15));
574   if (opac == 0) return;
575
576   _COMPOSITE_TILES(src, dst, dst_has_alpha, opac, luminosity);
577 }
578
579
580
581
582 // used to e.g. copy the background before starting to composite over it
583 //
584 // simply array copying (numpy assignment operator) is about 13 times slower, sadly
585 // The above comment is true when the array is sliced; it's only about two
586 // times faster now, in the current usecae.
587 void tile_copy_rgba16_into_rgba16(PyObject * src, PyObject * dst) {
588   PyArrayObject* src_arr = ((PyArrayObject*)src);
589   PyArrayObject* dst_arr = ((PyArrayObject*)dst);
590
591 #ifdef HEAVY_DEBUG
592   assert(PyArray_DIM(dst, 0) == TILE_SIZE);
593   assert(PyArray_DIM(dst, 1) == TILE_SIZE);
594   assert(PyArray_DIM(dst, 2) == 4);
595   assert(PyArray_TYPE(dst) == NPY_UINT16);
596   assert(PyArray_ISCARRAY(dst));
597   assert(dst_arr->strides[1] == 4*sizeof(uint16_t));
598   assert(dst_arr->strides[2] ==   sizeof(uint16_t));
599
600   assert(PyArray_DIM(src, 0) == TILE_SIZE);
601   assert(PyArray_DIM(src, 1) == TILE_SIZE);
602   assert(PyArray_DIM(src, 2) == 4);
603   assert(PyArray_TYPE(src) == NPY_UINT16);
604   assert(PyArray_ISCARRAY(dst));
605   assert(src_arr->strides[1] == 4*sizeof(uint16_t));
606   assert(src_arr->strides[2] ==   sizeof(uint16_t));
607 #endif
608
609   memcpy(dst_arr->data, src_arr->data, TILE_SIZE*TILE_SIZE*4*sizeof(uint16_t));
610   /* the code below can be used if it is not ISCARRAY, but only ISBEHAVED:
611   char * src_p = src_arr->data;
612   char * dst_p = dst_arr->data;
613   for (int y=0; y<TILE_SIZE; y++) {
614     memcpy(dst_p, src_p, TILE_SIZE*4);
615     src_p += src_arr->strides[0];
616     dst_p += dst_arr->strides[0];
617   }
618   */
619 }
620
621 void tile_clear(PyObject * dst) {
622   PyArrayObject* dst_arr = ((PyArrayObject*)dst);
623
624 #ifdef HEAVY_DEBUG
625   assert(PyArray_DIM(dst, 0) == TILE_SIZE);
626   assert(PyArray_DIM(dst, 1) == TILE_SIZE);
627   assert(PyArray_TYPE(dst) == NPY_UINT8);
628   assert(PyArray_ISBEHAVED(dst));
629   assert(dst_arr->strides[1] <= 8);
630 #endif
631
632   for (int y=0; y<TILE_SIZE; y++) {
633     uint8_t  * dst_p = (uint8_t*)(dst_arr->data + y*dst_arr->strides[0]);
634     memset(dst_p, 0, TILE_SIZE*dst_arr->strides[1]);
635     dst_p += dst_arr->strides[0];
636   }
637 }
638
639 // noise used for dithering (the same for each tile)
640 static const int dithering_noise_size = 64*64*2;
641 static uint16_t dithering_noise[dithering_noise_size];
642 static void precalculate_dithering_noise_if_required()
643 {
644   static bool have_noise = false;
645   if (!have_noise) {
646     // let's make some noise
647     for (int i=0; i<dithering_noise_size; i++) {
648       // random number in range [0.03 .. 0.97] * (1<<15)
649       //
650       // We could use the full range, but like this it is much easier
651       // to guarantee 8bpc load-save roundtrips don't alter the
652       // image. With the full range we would have to pay a lot
653       // attention to rounding converting 8bpc to our internal format.
654       dithering_noise[i] = (rand() % (1<<15)) * 240/256 + (1<<15) * 8/256;
655     }
656     have_noise = true;
657   }
658 }
659
660 // used mainly for saving layers (transparent PNG)
661 void tile_convert_rgba16_to_rgba8(PyObject * src, PyObject * dst) {
662   PyArrayObject* src_arr = ((PyArrayObject*)src);
663   PyArrayObject* dst_arr = ((PyArrayObject*)dst);
664
665 #ifdef HEAVY_DEBUG
666   assert(PyArray_DIM(dst, 0) == TILE_SIZE);
667   assert(PyArray_DIM(dst, 1) == TILE_SIZE);
668   assert(PyArray_DIM(dst, 2) == 4);
669   assert(PyArray_TYPE(dst) == NPY_UINT8);
670   assert(PyArray_ISBEHAVED(dst));
671   assert(dst_arr->strides[1] == 4*sizeof(uint8_t));
672   assert(dst_arr->strides[2] ==   sizeof(uint8_t));
673
674   assert(PyArray_DIM(src, 0) == TILE_SIZE);
675   assert(PyArray_DIM(src, 1) == TILE_SIZE);
676   assert(PyArray_DIM(src, 2) == 4);
677   assert(PyArray_TYPE(src) == NPY_UINT16);
678   assert(PyArray_ISBEHAVED(src));
679   assert(src_arr->strides[1] == 4*sizeof(uint16_t));
680   assert(src_arr->strides[2] ==   sizeof(uint16_t));
681 #endif
682
683   precalculate_dithering_noise_if_required();
684   int noise_idx = 0;
685
686   for (int y=0; y<TILE_SIZE; y++) {
687     uint16_t * src_p = (uint16_t*)(src_arr->data + y*src_arr->strides[0]);
688     uint8_t  * dst_p = (uint8_t*)(dst_arr->data + y*dst_arr->strides[0]);
689     for (int x=0; x<TILE_SIZE; x++) {
690       uint32_t r, g, b, a;
691       r = *src_p++;
692       g = *src_p++;
693       b = *src_p++;
694       a = *src_p++;
695 #ifdef HEAVY_DEBUG
696       assert(a<=(1<<15));
697       assert(r<=(1<<15));
698       assert(g<=(1<<15));
699       assert(b<=(1<<15));
700       assert(r<=a);
701       assert(g<=a);
702       assert(b<=a);
703 #endif
704       // un-premultiply alpha (with rounding)
705       if (a != 0) {
706         r = ((r << 15) + a/2) / a;
707         g = ((g << 15) + a/2) / a;
708         b = ((b << 15) + a/2) / a;
709       } else {
710         r = g = b = 0;
711       }
712 #ifdef HEAVY_DEBUG
713       assert(a<=(1<<15));
714       assert(r<=(1<<15));
715       assert(g<=(1<<15));
716       assert(b<=(1<<15));
717 #endif
718
719       /*
720       // Variant A) rounding
721       const uint32_t add_r = (1<<15)/2;
722       const uint32_t add_g = (1<<15)/2;
723       const uint32_t add_b = (1<<15)/2;
724       const uint32_t add_a = (1<<15)/2;
725       */
726       
727       /*
728       // Variant B) naive dithering
729       // This can alter the alpha channel during a load->save cycle.
730       const uint32_t add_r = rand() % (1<<15);
731       const uint32_t add_g = rand() % (1<<15);
732       const uint32_t add_b = rand() % (1<<15);
733       const uint32_t add_a = rand() % (1<<15);
734       */
735
736       /*
737       // Variant C) slightly better dithering
738       // make sure we don't dither rounding errors (those did occur when converting 8bit-->16bit)
739       // this preserves the alpha channel, but we still add noise to the highly transparent colors
740       const uint32_t add_r = (rand() % (1<<15)) * 240/256 + (1<<15) * 8/256;
741       const uint32_t add_g = add_r; // hm... do not produce too much color noise
742       const uint32_t add_b = add_r;
743       const uint32_t add_a = (rand() % (1<<15)) * 240/256 + (1<<15) * 8/256;
744       // TODO: error diffusion might work better than random dithering...
745       */
746
747       // Variant C) but with precalculated noise (much faster)
748       //
749       const uint32_t add_r = dithering_noise[noise_idx++];
750       const uint32_t add_g = add_r; // hm... do not produce too much color noise
751       const uint32_t add_b = add_r;
752       const uint32_t add_a = dithering_noise[noise_idx++];
753
754 #ifdef HEAVY_DEBUG
755       assert(add_a < (1<<15));
756       assert(add_a >= 0);
757       assert(noise_idx <= dithering_noise_size);
758 #endif
759
760       *dst_p++ = (r * 255 + add_r) / (1<<15);
761       *dst_p++ = (g * 255 + add_g) / (1<<15);
762       *dst_p++ = (b * 255 + add_b) / (1<<15);
763       *dst_p++ = (a * 255 + add_a) / (1<<15);
764     }
765     src_p += src_arr->strides[0];
766     dst_p += dst_arr->strides[0];
767   }
768 }
769
770 // used after compositing (when displaying, or when saving solid PNG or JPG)
771 void tile_convert_rgbu16_to_rgbu8(PyObject * src, PyObject * dst) {
772   PyArrayObject* src_arr = ((PyArrayObject*)src);
773   PyArrayObject* dst_arr = ((PyArrayObject*)dst);
774
775 #ifdef HEAVY_DEBUG
776   assert(PyArray_DIM(dst, 0) == TILE_SIZE);
777   assert(PyArray_DIM(dst, 1) == TILE_SIZE);
778   assert(PyArray_DIM(dst, 2) == 4);
779   assert(PyArray_TYPE(dst) == NPY_UINT8);
780   assert(PyArray_ISBEHAVED(dst));
781   assert(PyArray_STRIDE(dst, 1) == 4*sizeof(uint8_t));
782   assert(PyArray_STRIDE(dst, 2) == sizeof(uint8_t));
783
784   assert(PyArray_DIM(src, 0) == TILE_SIZE);
785   assert(PyArray_DIM(src, 1) == TILE_SIZE);
786   assert(PyArray_DIM(src, 2) == 4);
787   assert(PyArray_TYPE(src) == NPY_UINT16);
788   assert(PyArray_ISBEHAVED(src));
789   assert(PyArray_STRIDE(src, 1) == 4*sizeof(uint16_t));
790   assert(PyArray_STRIDE(src, 2) ==   sizeof(uint16_t));
791 #endif
792
793   precalculate_dithering_noise_if_required();
794   int noise_idx = 0;
795
796   for (int y=0; y<TILE_SIZE; y++) {
797     uint16_t * src_p = (uint16_t*)(src_arr->data + y*src_arr->strides[0]);
798     uint8_t  * dst_p = (uint8_t*)(dst_arr->data + y*dst_arr->strides[0]);
799     for (int x=0; x<TILE_SIZE; x++) {
800       uint32_t r, g, b;
801       r = *src_p++;
802       g = *src_p++;
803       b = *src_p++;
804       src_p++; // alpha unused
805 #ifdef HEAVY_DEBUG
806       assert(r<=(1<<15));
807       assert(g<=(1<<15));
808       assert(b<=(1<<15));
809 #endif
810       
811       /*
812       // rounding
813       const uint32_t add = (1<<15)/2;
814       */
815       // dithering
816       const uint32_t add = dithering_noise[noise_idx++];
817       
818       *dst_p++ = (r * 255 + add) / (1<<15);
819       *dst_p++ = (g * 255 + add) / (1<<15);
820       *dst_p++ = (b * 255 + add) / (1<<15);
821       *dst_p++ = 255;
822     }
823 #ifdef HEAVY_DEBUG
824     assert(noise_idx <= dithering_noise_size);
825 #endif
826     src_p += src_arr->strides[0];
827     dst_p += dst_arr->strides[0];
828   }
829 }
830
831 // used mainly for loading layers (transparent PNG)
832 void tile_convert_rgba8_to_rgba16(PyObject * src, PyObject * dst) {
833   PyArrayObject* src_arr = ((PyArrayObject*)src);
834   PyArrayObject* dst_arr = ((PyArrayObject*)dst);
835
836 #ifdef HEAVY_DEBUG
837   assert(PyArray_DIM(dst, 0) == TILE_SIZE);
838   assert(PyArray_DIM(dst, 1) == TILE_SIZE);
839   assert(PyArray_DIM(dst, 2) == 4);
840   assert(PyArray_TYPE(dst) == NPY_UINT16);
841   assert(PyArray_ISBEHAVED(dst));
842   assert(dst_arr->strides[1] == 4*sizeof(uint16_t));
843   assert(dst_arr->strides[2] ==   sizeof(uint16_t));
844
845   assert(PyArray_DIM(src, 0) == TILE_SIZE);
846   assert(PyArray_DIM(src, 1) == TILE_SIZE);
847   assert(PyArray_DIM(src, 2) == 4);
848   assert(PyArray_TYPE(src) == NPY_UINT8);
849   assert(PyArray_ISBEHAVED(src));
850   assert(src_arr->strides[1] == 4*sizeof(uint8_t));
851   assert(src_arr->strides[2] ==   sizeof(uint8_t));
852 #endif
853
854   for (int y=0; y<TILE_SIZE; y++) {
855     uint8_t  * src_p = (uint8_t*)(src_arr->data + y*src_arr->strides[0]);
856     uint16_t * dst_p = (uint16_t*)(dst_arr->data + y*dst_arr->strides[0]);
857     for (int x=0; x<TILE_SIZE; x++) {
858       uint32_t r, g, b, a;
859       r = *src_p++;
860       g = *src_p++;
861       b = *src_p++;
862       a = *src_p++;
863
864       // convert to fixed point (with rounding)
865       r = (r * (1<<15) + 255/2) / 255;
866       g = (g * (1<<15) + 255/2) / 255;
867       b = (b * (1<<15) + 255/2) / 255;
868       a = (a * (1<<15) + 255/2) / 255;
869
870       // premultiply alpha (with rounding), save back
871       *dst_p++ = (r * a + (1<<15)/2) / (1<<15);
872       *dst_p++ = (g * a + (1<<15)/2) / (1<<15);
873       *dst_p++ = (b * a + (1<<15)/2) / (1<<15);
874       *dst_p++ = a;
875     }
876   }
877 }
878
879 // used in strokemap.py
880 //
881 // Calculates a 1-bit bitmap of the stroke shape using two snapshots
882 // of the layer (the layer before and after the stroke).
883 //
884 // If the alpha increases a lot, we want the stroke to appear in
885 // the strokemap, even if the color did not change. If the alpha
886 // decreases a lot, we want to ignore the stroke (eraser). If
887 // the alpha decreases just a little, but the color changes a
888 // lot (eg. heavy smudging or watercolor brushes) we want the
889 // stroke still to be pickable.
890 //
891 // If the layer alpha was (near) zero, we record the stroke even if it
892 // is barely visible. This gives a bigger target to point-and-select.
893 //
894 void tile_perceptual_change_strokemap(PyObject * a, PyObject * b, PyObject * res) {
895
896   assert(PyArray_TYPE(a) == NPY_UINT16);
897   assert(PyArray_TYPE(b) == NPY_UINT16);
898   assert(PyArray_TYPE(res) == NPY_UINT8);
899   assert(PyArray_ISCARRAY(a));
900   assert(PyArray_ISCARRAY(b));
901   assert(PyArray_ISCARRAY(res));
902
903   uint16_t * a_p  = (uint16_t*)PyArray_DATA(a);
904   uint16_t * b_p  = (uint16_t*)PyArray_DATA(b);
905   uint8_t * res_p = (uint8_t*)PyArray_DATA(res);
906
907   for (int y=0; y<TILE_SIZE; y++) {
908     for (int x=0; x<TILE_SIZE; x++) {
909
910       int32_t color_change = 0;
911       // We want to compare a.color with b.color, but we only know
912       // (a.color * a.alpha) and (b.color * b.alpha).  We multiply
913       // each component with the alpha of the other image, so they are
914       // scaled the same and can be compared.
915
916       for (int i=0; i<3; i++) {
917         int32_t a_col = (uint32_t)a_p[i] * b_p[3] / (1<<15); // a.color * a.alpha*b.alpha
918         int32_t b_col = (uint32_t)b_p[i] * a_p[3] / (1<<15); // b.color * a.alpha*b.alpha
919         color_change += abs(b_col - a_col);
920       }
921       // "color_change" is in the range [0, 3*a_a]
922       // if either old or new alpha is (near) zero, "color_change" is (near) zero
923
924       int32_t alpha_old = a_p[3];
925       int32_t alpha_new = b_p[3];
926
927       // Note: the thresholds below are arbitrary choices found to work okay
928
929       // We report a color change only if both old and new color are
930       // well-defined (big enough alpha).
931       bool is_perceptual_color_change = color_change > MAX(alpha_old, alpha_new)/16;
932
933       int32_t alpha_diff = alpha_new - alpha_old; // no abs() here (ignore erasers)
934       // We check the alpha increase relative to the previous alpha.
935       bool is_perceptual_alpha_increase = alpha_diff > (1<<15)/4;
936
937       // this one is responsible for making fat big ugly easy-to-hit pointer targets
938       bool is_big_relative_alpha_increase  = alpha_diff > (1<<15)/64 && alpha_diff > alpha_old/2;
939
940       if (is_perceptual_alpha_increase || is_big_relative_alpha_increase || is_perceptual_color_change) {
941         res_p[0] = 1;
942       } else {
943         res_p[0] = 0;
944       }
945
946       a_p += 4;
947       b_p += 4;
948       res_p += 1;
949     }
950   }
951 }
952