1 /* This file is part of MyPaint.
2  * Copyright (C) 2008-2014 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 #include "pixops.hpp"
11 
12 #include "common.hpp"
13 #include "compositing.hpp"
14 #include "blending.hpp"
15 #include "fastapprox/fastpow.h"
16 
17 #include <mypaint-tiled-surface.h>
18 
19 #include <glib.h>
20 #include <math.h>
21 
22 #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
23 #define NO_IMPORT_ARRAY
24 #include <numpy/arrayobject.h>
25 
26 #include <stdlib.h>
27 #include <math.h>
28 
29 
30 void
tile_downscale_rgba16_c(const uint16_t * src,int src_strides,uint16_t * dst,int dst_strides,int dst_x,int dst_y)31 tile_downscale_rgba16_c(const uint16_t *src, int src_strides, uint16_t *dst,
32                         int dst_strides, int dst_x, int dst_y)
33 {
34   for (int y=0; y<MYPAINT_TILE_SIZE/2; y++) {
35     uint16_t * src_p = (uint16_t*)((char *)src + (2*y)*src_strides);
36     uint16_t * dst_p = (uint16_t*)((char *)dst + (y+dst_y)*dst_strides);
37     dst_p += 4*dst_x;
38     for(int x=0; x<MYPAINT_TILE_SIZE/2; x++) {
39       dst_p[0] = src_p[0]/4 + (src_p+4)[0]/4 + (src_p+4*MYPAINT_TILE_SIZE)[0]/4 + (src_p+4*MYPAINT_TILE_SIZE+4)[0]/4;
40       dst_p[1] = src_p[1]/4 + (src_p+4)[1]/4 + (src_p+4*MYPAINT_TILE_SIZE)[1]/4 + (src_p+4*MYPAINT_TILE_SIZE+4)[1]/4;
41       dst_p[2] = src_p[2]/4 + (src_p+4)[2]/4 + (src_p+4*MYPAINT_TILE_SIZE)[2]/4 + (src_p+4*MYPAINT_TILE_SIZE+4)[2]/4;
42       dst_p[3] = src_p[3]/4 + (src_p+4)[3]/4 + (src_p+4*MYPAINT_TILE_SIZE)[3]/4 + (src_p+4*MYPAINT_TILE_SIZE+4)[3]/4;
43       src_p += 8;
44       dst_p += 4;
45     }
46   }
47 }
48 
tile_downscale_rgba16(PyObject * src,PyObject * dst,int dst_x,int dst_y)49 void tile_downscale_rgba16(PyObject *src, PyObject *dst, int dst_x, int dst_y) {
50 
51   PyArrayObject* src_arr = ((PyArrayObject*)src);
52   PyArrayObject* dst_arr = ((PyArrayObject*)dst);
53 
54 #ifdef HEAVY_DEBUG
55   assert(PyArray_Check(src));
56   assert(PyArray_DIM(src_arr, 0) == MYPAINT_TILE_SIZE);
57   assert(PyArray_DIM(src_arr, 1) == MYPAINT_TILE_SIZE);
58   assert(PyArray_DIM(src_arr, 2) == 4);
59   assert(PyArray_TYPE(src_arr) == NPY_UINT16);
60   assert(PyArray_ISCARRAY(src_arr));
61 
62   assert(PyArray_Check(dst));
63   assert(PyArray_DIM(dst_arr, 2) == 4);
64   assert(PyArray_TYPE(dst_arr) == NPY_UINT16);
65   assert(PyArray_ISCARRAY(dst_arr));
66 #endif
67 
68   tile_downscale_rgba16_c((uint16_t*)PyArray_DATA(src_arr), PyArray_STRIDES(src_arr)[0],
69                           (uint16_t*)PyArray_DATA(dst_arr), PyArray_STRIDES(dst_arr)[0],
70                           dst_x, dst_y);
71 
72 }
73 
74 
tile_copy_rgba16_into_rgba16_c(const uint16_t * src,uint16_t * dst)75 void tile_copy_rgba16_into_rgba16_c(const uint16_t *src, uint16_t *dst) {
76   memcpy(dst, src, MYPAINT_TILE_SIZE*MYPAINT_TILE_SIZE*4*sizeof(uint16_t));
77 }
78 
tile_copy_rgba16_into_rgba16(PyObject * src,PyObject * dst)79 void tile_copy_rgba16_into_rgba16(PyObject * src, PyObject * dst) {
80   PyArrayObject* src_arr = ((PyArrayObject*)src);
81   PyArrayObject* dst_arr = ((PyArrayObject*)dst);
82 
83 #ifdef HEAVY_DEBUG
84   assert(PyArray_Check(dst));
85   assert(PyArray_DIM(dst_arr, 0) == MYPAINT_TILE_SIZE);
86   assert(PyArray_DIM(dst_arr, 1) == MYPAINT_TILE_SIZE);
87   assert(PyArray_DIM(dst_arr, 2) == 4);
88   assert(PyArray_TYPE(dst_arr) == NPY_UINT16);
89   assert(PyArray_ISCARRAY(dst_arr));
90   assert(PyArray_STRIDES(dst_arr)[1] == 4*sizeof(uint16_t));
91   assert(PyArray_STRIDES(dst_arr)[2] ==   sizeof(uint16_t));
92 
93   assert(PyArray_Check(src));
94   assert(PyArray_DIM(src_arr, 0) == MYPAINT_TILE_SIZE);
95   assert(PyArray_DIM(src_arr, 1) == MYPAINT_TILE_SIZE);
96   assert(PyArray_DIM(src_arr, 2) == 4);
97   assert(PyArray_TYPE(src_arr) == NPY_UINT16);
98   assert(PyArray_ISCARRAY(src_arr));
99   assert(PyArray_STRIDES(src_arr)[1] == 4*sizeof(uint16_t));
100   assert(PyArray_STRIDES(src_arr)[2] ==   sizeof(uint16_t));
101 #endif
102 
103   /* the code below can be used if it is not ISCARRAY, but only ISBEHAVED:
104   char * src_p = PyArray_DATA(src_arr);
105   char * dst_p = PyArray_DATA(dst_arr);
106   for (int y=0; y<MYPAINT_TILE_SIZE; y++) {
107     memcpy(dst_p, src_p, MYPAINT_TILE_SIZE*4);
108     src_p += src_arr->strides[0];
109     dst_p += dst_arr->strides[0];
110   }
111   */
112 
113   tile_copy_rgba16_into_rgba16_c((uint16_t *)PyArray_DATA(src_arr),
114                                  (uint16_t *)PyArray_DATA(dst_arr));
115 }
116 
tile_clear_rgba8(PyObject * dst)117 void tile_clear_rgba8(PyObject * dst) {
118   PyArrayObject* dst_arr = ((PyArrayObject*)dst);
119 
120 #ifdef HEAVY_DEBUG
121   assert(PyArray_Check(dst));
122   assert(PyArray_DIM(dst_arr, 0) == MYPAINT_TILE_SIZE);
123   assert(PyArray_DIM(dst_arr, 1) == MYPAINT_TILE_SIZE);
124   assert(PyArray_TYPE(dst_arr) == NPY_UINT8);
125   assert(PyArray_ISBEHAVED(dst_arr));
126   assert(PyArray_STRIDES(dst_arr)[1] <= 8);
127 #endif
128 
129   for (int y=0; y<MYPAINT_TILE_SIZE; y++) {
130     uint8_t  * dst_p = (uint8_t*)((char *)PyArray_DATA(dst_arr) + y*PyArray_STRIDES(dst_arr)[0]);
131     memset(dst_p, 0, MYPAINT_TILE_SIZE*PyArray_STRIDES(dst_arr)[1]);
132     dst_p += PyArray_STRIDES(dst_arr)[0];
133   }
134 }
135 
tile_clear_rgba16(PyObject * dst)136 void tile_clear_rgba16(PyObject * dst) {
137   PyArrayObject* dst_arr = ((PyArrayObject*)dst);
138 
139 #ifdef HEAVY_DEBUG
140   assert(PyArray_Check(dst));
141   assert(PyArray_DIM(dst_arr, 0) == MYPAINT_TILE_SIZE);
142   assert(PyArray_DIM(dst_arr, 1) == MYPAINT_TILE_SIZE);
143   assert(PyArray_TYPE(dst_arr) == NPY_UINT16);
144   assert(PyArray_ISBEHAVED(dst_arr));
145   assert(PyArray_STRIDES(dst_arr)[1] <= 8);
146 #endif
147 
148   for (int y=0; y<MYPAINT_TILE_SIZE; y++) {
149     uint16_t  * dst_p = (uint16_t*)((char *)PyArray_DATA(dst_arr) + y*PyArray_STRIDES(dst_arr)[0]);
150     memset(dst_p, 0, MYPAINT_TILE_SIZE*PyArray_STRIDES(dst_arr)[1]);
151     dst_p += PyArray_STRIDES(dst_arr)[0];
152   }
153 }
154 
155 
156 // Noise used for dithering (the same for each tile).
157 
158 static const int dithering_noise_size = MYPAINT_TILE_SIZE*MYPAINT_TILE_SIZE*4;
159 static uint16_t dithering_noise[dithering_noise_size];
precalculate_dithering_noise_if_required()160 static void precalculate_dithering_noise_if_required()
161 {
162   static bool have_noise = false;
163   if (!have_noise) {
164     // let's make some noise
165     for (int i=0; i<dithering_noise_size; i++) {
166       // random number in range [0.03 .. 0.2] * (1<<15)
167       //
168       // We could use the full range, but like this it is much easier
169       // to guarantee 8bpc load-save roundtrips don't alter the
170       // image. With the full range we would have to pay a lot
171       // attention to rounding converting 8bpc to our internal format.
172       dithering_noise[i] = (rand() % (1<<15)) * 5/256 + (1<<15) * 2/256;
173     }
174     have_noise = true;
175   }
176 }
177 
178 // Used for saving layers (transparent PNG), and for display when there
179 // can be transparent areas in the output.
180 
181 static inline void
tile_convert_rgba16_to_rgba8_const_c(const uint16_t * const src,const int src_strides,const uint8_t * dst,const int dst_strides)182 tile_convert_rgba16_to_rgba8_const_c (const uint16_t* const src,
183 				      const int src_strides,
184 				      const uint8_t* dst,
185 				      const int dst_strides)
186 {
187   precalculate_dithering_noise_if_required();
188 
189   for (int y=0; y<MYPAINT_TILE_SIZE; y++) {
190     int noise_idx = y*MYPAINT_TILE_SIZE*4;
191     const uint16_t *src_p = (uint16_t*)((char *)src + y*src_strides);
192     uint8_t *dst_p = (uint8_t*)((char *)dst + y*dst_strides);
193     for (int x=0; x<MYPAINT_TILE_SIZE; x++) {
194       uint32_t r, g, b, a;
195       r = *src_p++;
196       g = *src_p++;
197       b = *src_p++;
198       a = *src_p++;
199       // un-premultiply alpha (with rounding)
200       if (a != 0) {
201         const uint32_t rnd_a = a/2;
202         r = ((r << 15) + rnd_a) / a;
203         g = ((g << 15) + rnd_a) / a;
204         b = ((b << 15) + rnd_a) / a;
205       } else {
206         r = g = b = 0;
207       }
208       const uint32_t add_r = dithering_noise[noise_idx+0];
209       const uint32_t add_g = add_r; // hm... do not produce too much color noise
210       const uint32_t add_b = add_r;
211       const uint32_t add_a = dithering_noise[noise_idx+1];
212       noise_idx += 4;
213 
214 
215       *dst_p++ = (r * 255 + add_r) / (1<<15);
216       *dst_p++ = (g * 255 + add_g) / (1<<15);
217       *dst_p++ = (b * 255 + add_b) / (1<<15);
218       *dst_p++ = (a * 255 + add_a) / (1<<15);
219     }
220     src_p += src_strides;
221     dst_p += dst_strides;
222   }
223 }
224 
225 
226 static inline void
tile_convert_rgba16_to_rgba8_c(const uint16_t * const src,const int src_strides,const uint8_t * dst,const int dst_strides,const float EOTF)227 tile_convert_rgba16_to_rgba8_c (const uint16_t* const src,
228                                 const int src_strides,
229                                 const uint8_t* dst,
230                                 const int dst_strides,
231                                 const float EOTF)
232 {
233   if (EOTF == 1.0) {
234     tile_convert_rgba16_to_rgba8_const_c(src, src_strides, dst, dst_strides);
235     return;
236   }
237   precalculate_dithering_noise_if_required();
238 
239   for (int y=0; y<MYPAINT_TILE_SIZE; y++) {
240     int noise_idx = y*MYPAINT_TILE_SIZE*4;
241     const uint16_t *src_p = (uint16_t*)((char *)src + y*src_strides);
242     uint8_t *dst_p = (uint8_t*)((char *)dst + y*dst_strides);
243     for (int x=0; x<MYPAINT_TILE_SIZE; x++) {
244       uint32_t r, g, b, a;
245       r = *src_p++;
246       g = *src_p++;
247       b = *src_p++;
248       a = *src_p++;
249 #ifdef HEAVY_DEBUG
250       assert(a<=(1<<15));
251       assert(r<=(1<<15));
252       assert(g<=(1<<15));
253       assert(b<=(1<<15));
254       assert(r<=a);
255       assert(g<=a);
256       assert(b<=a);
257 #endif
258       // un-premultiply alpha (with rounding)
259       if (a != 0) {
260         const uint32_t rnd_a = a/2;
261         r = ((r << 15) + rnd_a) / a;
262         g = ((g << 15) + rnd_a) / a;
263         b = ((b << 15) + rnd_a) / a;
264       } else {
265         r = g = b = 0;
266       }
267 #ifdef HEAVY_DEBUG
268       assert(a<=(1<<15));
269       assert(r<=(1<<15));
270       assert(g<=(1<<15));
271       assert(b<=(1<<15));
272 #endif
273       /*
274       // Variant A) rounding
275       const uint32_t add_r = (1<<15)/2;
276       const uint32_t add_g = (1<<15)/2;
277       const uint32_t add_b = (1<<15)/2;
278       const uint32_t add_a = (1<<15)/2;
279       */
280 
281       /*
282       // Variant B) naive dithering
283       // This can alter the alpha channel during a load->save cycle.
284       const uint32_t add_r = rand() % (1<<15);
285       const uint32_t add_g = rand() % (1<<15);
286       const uint32_t add_b = rand() % (1<<15);
287       const uint32_t add_a = rand() % (1<<15);
288       */
289 
290       /*
291       // Variant C) slightly better dithering
292       // make sure we don't dither rounding errors (those did occur when converting 8bit-->16bit)
293       // this preserves the alpha channel, but we still add noise to the highly transparent colors
294       const uint32_t add_r = (rand() % (1<<15)) * 240/256 + (1<<15) * 8/256;
295       const uint32_t add_g = add_r; // hm... do not produce too much color noise
296       const uint32_t add_b = add_r;
297       const uint32_t add_a = (rand() % (1<<15)) * 240/256 + (1<<15) * 8/256;
298       // TODO: error diffusion might work better than random dithering...
299       */
300 
301       // Variant C) but with precalculated noise (much faster)
302       //
303       const float add_r = (float)dithering_noise[noise_idx+0] / (1<<30);
304       //const uint32_t add_g = add_r; // hm... do not produce too much color noise
305       //const uint32_t add_b = add_r;
306       const uint32_t add_a = dithering_noise[noise_idx+1];
307       noise_idx += 4;
308 
309 #ifdef HEAVY_DEBUG
310       assert(add_a < (1<<15));
311       assert(add_a >= 0);
312       assert(noise_idx <= dithering_noise_size);
313 #endif
314 
315       *dst_p++ = uint8_t(fastpow((float)r / (1<<15) + add_r, 1.0/EOTF) * 255 + 0.5);
316       *dst_p++ = uint8_t(fastpow((float)g / (1<<15) + add_r, 1.0/EOTF) * 255 + 0.5);
317       *dst_p++ = uint8_t(fastpow((float)b / (1<<15) + add_r, 1.0/EOTF) * 255 + 0.5);
318       *dst_p++ = ((a * 255 + add_a) / (1<<15));
319     }
320     src_p += src_strides;
321     dst_p += dst_strides;
322   }
323 }
324 
325 
326 
327 void
tile_convert_rgba16_to_rgba8(PyObject * src,PyObject * dst,const float EOTF)328 tile_convert_rgba16_to_rgba8 (PyObject *src,
329                               PyObject *dst, const float EOTF)
330 {
331   PyArrayObject* src_arr = ((PyArrayObject*)src);
332   PyArrayObject* dst_arr = ((PyArrayObject*)dst);
333 
334 #ifdef HEAVY_DEBUG
335   assert(PyArray_Check(dst));
336   assert(PyArray_DIM(dst_arr, 0) == MYPAINT_TILE_SIZE);
337   assert(PyArray_DIM(dst_arr, 1) == MYPAINT_TILE_SIZE);
338   assert(PyArray_DIM(dst_arr, 2) == 4);
339   assert(PyArray_TYPE(dst_arr) == NPY_UINT8);
340   assert(PyArray_ISBEHAVED(dst_arr));
341   assert(PyArray_STRIDE(dst_arr, 1) == 4*sizeof(uint8_t));
342   assert(PyArray_STRIDE(dst_arr, 2) == sizeof(uint8_t));
343 
344   assert(PyArray_Check(src));
345   assert(PyArray_DIM(src_arr, 0) == MYPAINT_TILE_SIZE);
346   assert(PyArray_DIM(src_arr, 1) == MYPAINT_TILE_SIZE);
347   assert(PyArray_DIM(src_arr, 2) == 4);
348   assert(PyArray_TYPE(src_arr) == NPY_UINT16);
349   assert(PyArray_ISBEHAVED(src_arr));
350   assert(PyArray_STRIDE(src_arr, 1) == 4*sizeof(uint16_t));
351   assert(PyArray_STRIDE(src_arr, 2) ==   sizeof(uint16_t));
352 #endif
353 
354   tile_convert_rgba16_to_rgba8_c((uint16_t*)PyArray_DATA(src_arr),
355                                  PyArray_STRIDES(src_arr)[0],
356                                  (uint8_t*)PyArray_DATA(dst_arr),
357                                  PyArray_STRIDES(dst_arr)[0],
358                                  EOTF);
359 }
360 
361 static inline void
tile_convert_rgbu16_to_rgbu8_const_c(const uint16_t * const src,const int src_strides,const uint8_t * dst,const int dst_strides)362 tile_convert_rgbu16_to_rgbu8_const_c(const uint16_t *const src,
363                                      const int src_strides,
364 				     const uint8_t *dst,
365                                      const int dst_strides)
366 {
367   precalculate_dithering_noise_if_required();
368 
369   for (int y=0; y<MYPAINT_TILE_SIZE; y++) {
370     int noise_idx = y*MYPAINT_TILE_SIZE*4;
371     const uint16_t *src_p = (uint16_t*)((char *)src + y*src_strides);
372     uint8_t *dst_p = (uint8_t*)((char *)dst + y*dst_strides);
373     for (int x=0; x<MYPAINT_TILE_SIZE; x++) {
374       uint32_t r, g, b;
375       r = *src_p++;
376       g = *src_p++;
377       b = *src_p++;
378       src_p++;
379       const uint32_t add = dithering_noise[noise_idx++];
380       *dst_p++ = (r * 255 + add) / (1<<15);
381       *dst_p++ = (g * 255 + add) / (1<<15);
382       *dst_p++ = (b * 255 + add) / (1<<15);
383       *dst_p++ = 255;
384     }
385     src_p += src_strides;
386     dst_p += dst_strides;
387   }
388 }
389 
390 static inline void
tile_convert_rgbu16_to_rgbu8_c(const uint16_t * const src,const int src_strides,const uint8_t * dst,const int dst_strides,const float EOTF)391 tile_convert_rgbu16_to_rgbu8_c(const uint16_t* const src,
392                                const int src_strides,
393                                const uint8_t* dst,
394                                const int dst_strides,
395                                const float EOTF)
396 {
397   if (EOTF == 1.0) {
398     tile_convert_rgbu16_to_rgbu8_const_c(src, src_strides, dst, dst_strides);
399     return;
400   }
401   precalculate_dithering_noise_if_required();
402 
403   for (int y=0; y<MYPAINT_TILE_SIZE; y++) {
404     int noise_idx = y*MYPAINT_TILE_SIZE*4;
405     const uint16_t *src_p = (uint16_t*)((char *)src + y*src_strides);
406     uint8_t *dst_p = (uint8_t*)((char *)dst + y*dst_strides);
407     for (int x=0; x<MYPAINT_TILE_SIZE; x++) {
408       float r, g, b;
409       r = ((float)*src_p++ / (1<<15));
410       g = ((float)*src_p++ / (1<<15));
411       b = ((float)*src_p++ / (1<<15));
412       src_p++; // alpha unused
413 #ifdef HEAVY_DEBUG
414       assert(r<=(1<<15));
415       assert(g<=(1<<15));
416       assert(b<=(1<<15));
417 #endif
418 
419       /*
420       // rounding
421       const uint32_t add = (1<<15)/2;
422       */
423       // dithering
424       const float add = (float)dithering_noise[noise_idx++] / (1<<30);
425 
426       *dst_p++ = (fastpow(r + add, 1.0/EOTF) ) * 255 + 0.5;
427       *dst_p++ = (fastpow(g + add, 1.0/EOTF) ) * 255 + 0.5;
428       *dst_p++ = (fastpow(b + add, 1.0/EOTF) ) * 255 + 0.5;
429       *dst_p++ = 255;
430     }
431 #ifdef HEAVY_DEBUG
432     assert(noise_idx <= dithering_noise_size);
433 #endif
434     src_p += src_strides;
435     dst_p += dst_strides;
436   }
437 }
438 
439 
tile_convert_rgbu16_to_rgbu8(PyObject * src,PyObject * dst,const float EOTF)440 void tile_convert_rgbu16_to_rgbu8(PyObject * src, PyObject * dst, const float EOTF) {
441   PyArrayObject* src_arr = ((PyArrayObject*)src);
442   PyArrayObject* dst_arr = ((PyArrayObject*)dst);
443 
444 #ifdef HEAVY_DEBUG
445   assert(PyArray_Check(dst));
446   assert(PyArray_DIM(dst_arr, 0) == MYPAINT_TILE_SIZE);
447   assert(PyArray_DIM(dst_arr, 1) == MYPAINT_TILE_SIZE);
448   assert(PyArray_DIM(dst_arr, 2) == 4);
449   assert(PyArray_TYPE(dst_arr) == NPY_UINT8);
450   assert(PyArray_ISBEHAVED(dst_arr));
451   assert(PyArray_STRIDE(dst_arr, 1) == 4*sizeof(uint8_t));
452   assert(PyArray_STRIDE(dst_arr, 2) == sizeof(uint8_t));
453 
454   assert(PyArray_Check(src));
455   assert(PyArray_DIM(src_arr, 0) == MYPAINT_TILE_SIZE);
456   assert(PyArray_DIM(src_arr, 1) == MYPAINT_TILE_SIZE);
457   assert(PyArray_DIM(src_arr, 2) == 4);
458   assert(PyArray_TYPE(src_arr) == NPY_UINT16);
459   assert(PyArray_ISBEHAVED(src_arr));
460   assert(PyArray_STRIDE(src_arr, 1) == 4*sizeof(uint16_t));
461   assert(PyArray_STRIDE(src_arr, 2) ==   sizeof(uint16_t));
462 #endif
463 
464   tile_convert_rgbu16_to_rgbu8_c((uint16_t*)PyArray_DATA(src_arr), PyArray_STRIDES(src_arr)[0],
465                                  (uint8_t*)PyArray_DATA(dst_arr), PyArray_STRIDES(dst_arr)[0],
466                                   EOTF);
467 }
468 
tile_convert_rgba8_to_rgba16_const(PyObject * src,PyObject * dst)469 void tile_convert_rgba8_to_rgba16_const(PyObject * src, PyObject * dst) {
470   PyArrayObject* src_arr = ((PyArrayObject*)src);
471   PyArrayObject* dst_arr = ((PyArrayObject*)dst);
472   for (int y=0; y<MYPAINT_TILE_SIZE; y++) {
473     uint8_t  * src_p = (uint8_t*)((char *)PyArray_DATA(src_arr) + y*PyArray_STRIDES(src_arr)[0]);
474     uint16_t * dst_p = (uint16_t*)((char *)PyArray_DATA(dst_arr) + y*PyArray_STRIDES(dst_arr)[0]);
475     for (int x=0; x<MYPAINT_TILE_SIZE; x++) {
476       uint32_t r, g, b, a;
477       r = *src_p++;
478       g = *src_p++;
479       b = *src_p++;
480       a = *src_p++;
481 
482       // convert to fixed point (with rounding)
483       r = (r * (1<<15) + 255/2) / 255;
484       g = (g * (1<<15) + 255/2) / 255;
485       b = (b * (1<<15) + 255/2) / 255;
486       a = (a * (1<<15) + 255/2) / 255;
487 
488       // premultiply alpha (with rounding), save back
489       *dst_p++ = (r * a + (1<<15)/2) / (1<<15);
490       *dst_p++ = (g * a + (1<<15)/2) / (1<<15);
491       *dst_p++ = (b * a + (1<<15)/2) / (1<<15);
492       *dst_p++ = a;
493     }
494   }
495 }
496 
497 
498 // used mainly for loading layers (transparent PNG)
tile_convert_rgba8_to_rgba16(PyObject * src,PyObject * dst,const float EOTF)499 void tile_convert_rgba8_to_rgba16(PyObject * src, PyObject * dst, const float EOTF) {
500 
501   if (EOTF == 1.0) {
502     tile_convert_rgba8_to_rgba16_const(src, dst);
503     return;
504   }
505   PyArrayObject* src_arr = ((PyArrayObject*)src);
506   PyArrayObject* dst_arr = ((PyArrayObject*)dst);
507 
508 #ifdef HEAVY_DEBUG
509   assert(PyArray_Check(dst));
510   assert(PyArray_DIM(dst_arr, 0) == MYPAINT_TILE_SIZE);
511   assert(PyArray_DIM(dst_arr, 1) == MYPAINT_TILE_SIZE);
512   assert(PyArray_DIM(dst_arr, 2) == 4);
513   assert(PyArray_TYPE(dst_arr) == NPY_UINT16);
514   assert(PyArray_ISBEHAVED(dst_arr));
515   assert(PyArray_STRIDES(dst_arr)[1] == 4*sizeof(uint16_t));
516   assert(PyArray_STRIDES(dst_arr)[2] ==   sizeof(uint16_t));
517 
518   assert(PyArray_Check(src));
519   assert(PyArray_DIM(src_arr, 0) == MYPAINT_TILE_SIZE);
520   assert(PyArray_DIM(src_arr, 1) == MYPAINT_TILE_SIZE);
521   assert(PyArray_DIM(src_arr, 2) == 4);
522   assert(PyArray_TYPE(src_arr) == NPY_UINT8);
523   assert(PyArray_ISBEHAVED(src_arr));
524   assert(PyArray_STRIDES(src_arr)[1] == 4*sizeof(uint8_t));
525   assert(PyArray_STRIDES(src_arr)[2] ==   sizeof(uint8_t));
526 #endif
527 
528   for (int y=0; y<MYPAINT_TILE_SIZE; y++) {
529     uint8_t  * src_p = (uint8_t*)((char *)PyArray_DATA(src_arr) + y*PyArray_STRIDES(src_arr)[0]);
530     uint16_t * dst_p = (uint16_t*)((char *)PyArray_DATA(dst_arr) + y*PyArray_STRIDES(dst_arr)[0]);
531     for (int x=0; x<MYPAINT_TILE_SIZE; x++) {
532       uint32_t r, g, b, a;
533       r = *src_p++;
534       g = *src_p++;
535       b = *src_p++;
536       a = *src_p++;
537 
538       // convert to fixed point (with rounding)
539       r = uint32_t(fastpow((float)r/255.0, EOTF) * (1<<15) + 0.5);
540       g = uint32_t(fastpow((float)g/255.0, EOTF) * (1<<15) + 0.5);
541       b = uint32_t(fastpow((float)b/255.0, EOTF) * (1<<15) + 0.5);
542       a = (a * (1<<15) + 255/2) / 255;
543 
544       // premultiply alpha (with rounding), save back
545       *dst_p++ = (r * a + (1<<15)/2) / (1<<15);
546       *dst_p++ = (g * a + (1<<15)/2) / (1<<15);
547       *dst_p++ = (b * a + (1<<15)/2) / (1<<15);
548       *dst_p++ = a;
549     }
550   }
551 }
552 
553 
tile_rgba2flat(PyObject * dst_obj,PyObject * bg_obj)554 void tile_rgba2flat(PyObject * dst_obj, PyObject * bg_obj) {
555   PyArrayObject* bg = ((PyArrayObject*)bg_obj);
556   PyArrayObject* dst = ((PyArrayObject*)dst_obj);
557 
558 #ifdef HEAVY_DEBUG
559   assert(PyArray_Check(dst_obj));
560   assert(PyArray_DIM(dst, 0) == MYPAINT_TILE_SIZE);
561   assert(PyArray_DIM(dst, 1) == MYPAINT_TILE_SIZE);
562   assert(PyArray_DIM(dst, 2) == 4);
563   assert(PyArray_TYPE(dst) == NPY_UINT16);
564   assert(PyArray_ISCARRAY(dst));
565 
566   assert(PyArray_Check(bg_obj));
567   assert(PyArray_DIM(bg, 0) == MYPAINT_TILE_SIZE);
568   assert(PyArray_DIM(bg, 1) == MYPAINT_TILE_SIZE);
569   assert(PyArray_DIM(bg, 2) == 4);
570   assert(PyArray_TYPE(bg) == NPY_UINT16);
571   assert(PyArray_ISCARRAY(bg));
572 #endif
573 
574   uint16_t * dst_p  = (uint16_t*)PyArray_DATA(dst);
575   uint16_t * bg_p  = (uint16_t*)PyArray_DATA(bg);
576   for (int i=0; i<MYPAINT_TILE_SIZE*MYPAINT_TILE_SIZE; i++) {
577     // resultAlpha = 1.0 (thus it does not matter if resultColor is premultiplied alpha or not)
578     // resultColor = topColor + (1.0 - topAlpha) * bottomColor
579     const uint32_t one_minus_top_alpha = (1<<15) - dst_p[3];
580     dst_p[0] += ((uint32_t)one_minus_top_alpha*bg_p[0]) / (1<<15);
581     dst_p[1] += ((uint32_t)one_minus_top_alpha*bg_p[1]) / (1<<15);
582     dst_p[2] += ((uint32_t)one_minus_top_alpha*bg_p[2]) / (1<<15);
583     dst_p += 4;
584     bg_p += 4;
585   }
586 }
587 
588 
tile_flat2rgba(PyObject * dst_obj,PyObject * bg_obj)589 void tile_flat2rgba(PyObject * dst_obj, PyObject * bg_obj) {
590 
591   PyArrayObject *dst = (PyArrayObject *)dst_obj;
592   PyArrayObject *bg = (PyArrayObject *)bg_obj;
593 #ifdef HEAVY_DEBUG
594   assert(PyArray_Check(dst_obj));
595   assert(PyArray_DIM(dst, 0) == MYPAINT_TILE_SIZE);
596   assert(PyArray_DIM(dst, 1) == MYPAINT_TILE_SIZE);
597   assert(PyArray_DIM(dst, 2) == 4);
598   assert(PyArray_TYPE(dst) == NPY_UINT16);
599   assert(PyArray_ISCARRAY(dst));
600 
601   assert(PyArray_Check(bg_obj));
602   assert(PyArray_DIM(bg, 0) == MYPAINT_TILE_SIZE);
603   assert(PyArray_DIM(bg, 1) == MYPAINT_TILE_SIZE);
604   assert(PyArray_DIM(bg, 2) == 4);
605   assert(PyArray_TYPE(bg) == NPY_UINT16);
606   assert(PyArray_ISCARRAY(bg));
607 #endif
608 
609   uint16_t * dst_p  = (uint16_t*)PyArray_DATA(dst);
610   uint16_t * bg_p  = (uint16_t*)PyArray_DATA(bg);
611   for (int i=0; i<MYPAINT_TILE_SIZE*MYPAINT_TILE_SIZE; i++) {
612 
613     // 1. calculate final dst.alpha
614     uint16_t final_alpha = dst_p[3];
615     for (int i=0; i<3;i++) {
616       int32_t color_change = (int32_t)dst_p[i] - bg_p[i];
617       uint16_t minimal_alpha;
618       if (color_change > 0) {
619         minimal_alpha = (int64_t)color_change*(1<<15) / ((1<<15) - bg_p[i]);
620       } else if (color_change < 0) {
621         minimal_alpha = (int64_t)-color_change*(1<<15) / bg_p[i];
622       } else {
623         minimal_alpha = 0;
624       }
625       final_alpha = MAX(final_alpha, minimal_alpha);
626 #ifdef HEAVY_DEBUG
627       assert(minimal_alpha <= (1<<15));
628       assert(final_alpha   <= (1<<15));
629 #endif
630     }
631 
632     // 2. calculate dst.color and update dst
633     dst_p[3] = final_alpha;
634     if (final_alpha > 0) {
635       for (int i=0; i<3;i++) {
636         int32_t color_change = (int32_t)dst_p[i] - bg_p[i];
637         //int64_t res = bg_p[i] + (int64_t)color_change*(1<<15) / final_alpha;
638         // premultiplied with final_alpha
639         int64_t res = (uint32_t)bg_p[i]*final_alpha/(1<<15) + (int64_t)color_change;
640         res = CLAMP(res, 0, final_alpha); // fix rounding errors
641         dst_p[i] = res;
642 #ifdef HEAVY_DEBUG
643         assert(dst_p[i] <= dst_p[3]);
644 #endif
645       }
646     } else {
647       dst_p[0] = 0;
648       dst_p[1] = 0;
649       dst_p[2] = 0;
650     }
651     dst_p += 4;
652     bg_p += 4;
653   }
654 }
655 
656 
tile_perceptual_change_strokemap(PyObject * a_obj,PyObject * b_obj,PyObject * res_obj)657 void tile_perceptual_change_strokemap(PyObject * a_obj, PyObject * b_obj, PyObject * res_obj) {
658 
659   PyArrayObject *a = (PyArrayObject *)a_obj;
660   PyArrayObject *b = (PyArrayObject *)b_obj;
661   PyArrayObject *res = (PyArrayObject *)res_obj;
662 
663 #ifdef HEAVY_DEBUG
664   assert(PyArray_TYPE(a) == NPY_UINT16);
665   assert(PyArray_TYPE(b) == NPY_UINT16);
666   assert(PyArray_TYPE(res) == NPY_UINT8);
667   assert(PyArray_ISCARRAY(a));
668   assert(PyArray_ISCARRAY(b));
669   assert(PyArray_ISCARRAY(res));
670 #endif
671 
672   uint16_t * a_p  = (uint16_t*)PyArray_DATA(a);
673   uint16_t * b_p  = (uint16_t*)PyArray_DATA(b);
674   uint8_t * res_p = (uint8_t*)PyArray_DATA(res);
675 
676   for (int y=0; y<MYPAINT_TILE_SIZE; y++) {
677     for (int x=0; x<MYPAINT_TILE_SIZE; x++) {
678 
679       int32_t color_change = 0;
680       // We want to compare a.color with b.color, but we only know
681       // (a.color * a.alpha) and (b.color * b.alpha).  We multiply
682       // each component with the alpha of the other image, so they are
683       // scaled the same and can be compared.
684 
685       for (int i=0; i<3; i++) {
686         int32_t a_col = (uint32_t)a_p[i] * b_p[3] / (1<<15); // a.color * a.alpha*b.alpha
687         int32_t b_col = (uint32_t)b_p[i] * a_p[3] / (1<<15); // b.color * a.alpha*b.alpha
688         color_change += abs(b_col - a_col);
689       }
690       // "color_change" is in the range [0, 3*a_a]
691       // if either old or new alpha is (near) zero, "color_change" is (near) zero
692 
693       int32_t alpha_old = a_p[3];
694       int32_t alpha_new = b_p[3];
695 
696       // Note: the thresholds below are arbitrary choices found to work okay
697 
698       // We report a color change only if both old and new color are
699       // well-defined (big enough alpha).
700       bool is_perceptual_color_change = color_change > MAX(alpha_old, alpha_new)/16;
701 
702       int32_t alpha_diff = alpha_new - alpha_old; // no abs() here (ignore erasers)
703       // We check the alpha increase relative to the previous alpha.
704       bool is_perceptual_alpha_increase = alpha_diff > (1<<15)/4;
705 
706       // this one is responsible for making fat big ugly easy-to-hit pointer targets
707       bool is_big_relative_alpha_increase  = alpha_diff > (1<<15)/64 && alpha_diff > alpha_old/2;
708 
709       if (is_perceptual_alpha_increase || is_big_relative_alpha_increase || is_perceptual_color_change) {
710         res_p[0] = 1;
711       } else {
712         res_p[0] = 0;
713       }
714 
715       a_p += 4;
716       b_p += 4;
717       res_p += 1;
718     }
719   }
720 }
721 
722 
723 // A named tile combine operation: what the user sees as a "blend mode" or
724 // the "layer composite" modes in the application.
725 
726 template <class B, class C>
727 class TileDataCombine : public TileDataCombineOp
728 {
729   private:
730     // The canonical name for the combine mode
731     const char *name;
732     // Alpha/nonalpha functors; must be members to keep GCC4.6 builds happy
733     static const int bufsize = MYPAINT_TILE_SIZE*MYPAINT_TILE_SIZE*4;
734     BufferCombineFunc<true, bufsize, B, C> combine_dstalpha;
735     BufferCombineFunc<false, bufsize, B, C> combine_dstnoalpha;
736 
737   public:
TileDataCombine(const char * name)738     TileDataCombine(const char *name) {
739         this->name = name;
740     }
741 
742     // Apply this combine operation to source and destination tile-sized
743     // buffers of uint16_t (15ish-bit) RGBA data. The output is written back
744     // into the destination buffer.
combine_data(const fix15_short_t * src_p,fix15_short_t * dst_p,const bool dst_has_alpha,const float src_opacity) const745     void combine_data (const fix15_short_t *src_p,
746                        fix15_short_t *dst_p,
747                        const bool dst_has_alpha,
748                        const float src_opacity) const
749     {
750         const fix15_short_t opac = fix15_short_clamp(src_opacity * fix15_one);
751         if (dst_has_alpha) {
752             combine_dstalpha(src_p, dst_p, opac);
753         }
754         else {
755             combine_dstnoalpha(src_p, dst_p, opac);
756         }
757     }
758 
759     // True if a zero-alpha source pixel can ever affect a destination pixel
zero_alpha_has_effect() const760     bool zero_alpha_has_effect() const {
761         return C::zero_alpha_has_effect;
762     }
763 
764     // True if a source pixel can ever reduce the alpha of a destination pixel
can_decrease_alpha() const765     bool can_decrease_alpha() const {
766         return C::can_decrease_alpha;
767     }
768 
769     // True if a zero-alpha src pixel always clears the dst pixel
zero_alpha_clears_backdrop() const770     bool zero_alpha_clears_backdrop() const {
771         return C::zero_alpha_clears_backdrop;
772     }
773 
774     // Returns the canonical name of the mode
get_name() const775     const char* get_name() const {
776         return name;
777     }
778 };
779 
780 
781 // Integer-indexed LUT for the layer mode definitions, defining their canonical
782 // names.
783 
784 static const TileDataCombineOp * combine_mode_info[NumCombineModes] =
785 {
786     // Source-over compositing + various blend modes
787     new TileDataCombine<BlendNormal, CompositeSourceOver>("svg:src-over"),
788     new TileDataCombine<BlendMultiply, CompositeSourceOver>("svg:multiply"),
789     new TileDataCombine<BlendScreen, CompositeSourceOver>("svg:screen"),
790     new TileDataCombine<BlendOverlay, CompositeSourceOver>("svg:overlay"),
791     new TileDataCombine<BlendDarken, CompositeSourceOver>("svg:darken"),
792     new TileDataCombine<BlendLighten, CompositeSourceOver>("svg:lighten"),
793     new TileDataCombine<BlendHardLight, CompositeSourceOver>("svg:hard-light"),
794     new TileDataCombine<BlendSoftLight, CompositeSourceOver>("svg:soft-light"),
795     new TileDataCombine<BlendColorBurn, CompositeSourceOver>("svg:color-burn"),
796     new TileDataCombine<BlendColorDodge, CompositeSourceOver>("svg:color-dodge"),
797     new TileDataCombine<BlendDifference, CompositeSourceOver>("svg:difference"),
798     new TileDataCombine<BlendExclusion, CompositeSourceOver>("svg:exclusion"),
799     new TileDataCombine<BlendHue, CompositeSourceOver>("svg:hue"),
800     new TileDataCombine<BlendSaturation, CompositeSourceOver>("svg:saturation"),
801     new TileDataCombine<BlendColor, CompositeSourceOver>("svg:color"),
802     new TileDataCombine<BlendLuminosity, CompositeSourceOver>("svg:luminosity"),
803 
804     // Normal blend mode + various compositing operators
805     new TileDataCombine<BlendNormal, CompositeLighter>("svg:plus"),
806     new TileDataCombine<BlendNormal, CompositeDestinationIn>("svg:dst-in"),
807     new TileDataCombine<BlendNormal, CompositeDestinationOut>("svg:dst-out"),
808     new TileDataCombine<BlendNormal, CompositeSourceAtop>("svg:src-atop"),
809     new TileDataCombine<BlendNormal, CompositeDestinationAtop>("svg:dst-atop"),
810     new TileDataCombine<BlendNormal, CompositeSpectralWGM>("mypaint:spectral-wgm")
811 };
812 
813 
814 
815 /* combine_mode_get_info(): extracts Python-readable metadata for a mode */
816 
817 
818 PyObject *
combine_mode_get_info(enum CombineMode mode)819 combine_mode_get_info(enum CombineMode mode)
820 {
821     if (mode >= NumCombineModes || mode < 0) {
822         return Py_BuildValue("{}");
823     }
824     const TileDataCombineOp *op = combine_mode_info[mode];
825     return Py_BuildValue("{s:i,s:i,s:i,s:s}",
826             "zero_alpha_has_effect", op->zero_alpha_has_effect(),
827             "can_decrease_alpha", op->can_decrease_alpha(),
828             "zero_alpha_clears_backdrop", op->zero_alpha_clears_backdrop(),
829             "name", op->get_name()
830         );
831 }
832 
833 
834 
835 /* tile_combine(): primary Python interface for blending+compositing tiles */
836 
837 
838 void
tile_combine(enum CombineMode mode,PyObject * src_obj,PyObject * dst_obj,const bool dst_has_alpha,const float src_opacity)839 tile_combine (enum CombineMode mode,
840               PyObject *src_obj,
841               PyObject *dst_obj,
842               const bool dst_has_alpha,
843               const float src_opacity)
844 {
845     PyArrayObject* src = ((PyArrayObject*)src_obj);
846     PyArrayObject* dst = ((PyArrayObject*)dst_obj);
847 #ifdef HEAVY_DEBUG
848     assert(PyArray_Check(src_obj));
849     assert(PyArray_DIM(src, 0) == MYPAINT_TILE_SIZE);
850     assert(PyArray_DIM(src, 1) == MYPAINT_TILE_SIZE);
851     assert(PyArray_DIM(src, 2) == 4);
852     assert(PyArray_TYPE(src) == NPY_UINT16);
853     assert(PyArray_ISCARRAY(src));
854 
855     assert(PyArray_Check(dst_obj));
856     assert(PyArray_DIM(dst, 0) == MYPAINT_TILE_SIZE);
857     assert(PyArray_DIM(dst, 1) == MYPAINT_TILE_SIZE);
858     assert(PyArray_DIM(dst, 2) == 4);
859     assert(PyArray_TYPE(dst) == NPY_UINT16);
860     assert(PyArray_ISCARRAY(dst));
861 
862     assert(PyArray_STRIDES(dst)[0] == 4*sizeof(fix15_short_t)*MYPAINT_TILE_SIZE);
863     assert(PyArray_STRIDES(dst)[1] == 4*sizeof(fix15_short_t));
864     assert(PyArray_STRIDES(dst)[2] ==   sizeof(fix15_short_t));
865 #endif
866 
867     const fix15_short_t* const src_p = (fix15_short_t *)PyArray_DATA(src);
868     fix15_short_t*       const dst_p = (fix15_short_t *)PyArray_DATA(dst);
869 
870     if (mode >= NumCombineModes || mode < 0) {
871         return;
872     }
873     const TileDataCombineOp *op = combine_mode_info[mode];
874     op->combine_data(src_p, dst_p, dst_has_alpha, src_opacity);
875 }
876 
877