OSDN Git Service

4c722745763d3c5cf759b4745ecb7405df02974f
[mypaint-anime/master.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 // downscale a tile to half its size using bilinear interpolation
11 // used mainly for generating background mipmaps
12 void tile_downscale_rgb16(PyObject *src, PyObject *dst, int dst_x, int dst_y) {
13 #ifdef HEAVY_DEBUG
14   assert(PyArray_DIM(src, 0) == TILE_SIZE);
15   assert(PyArray_DIM(src, 1) == TILE_SIZE);
16   assert(PyArray_TYPE(src) == NPY_UINT16);
17   assert(PyArray_ISCARRAY(src));
18
19   assert(PyArray_TYPE(dst) == NPY_UINT16);
20   assert(PyArray_ISCARRAY(dst));
21 #endif
22
23   PyArrayObject* src_arr = ((PyArrayObject*)src);
24   PyArrayObject* dst_arr = ((PyArrayObject*)dst);
25
26   for (int y=0; y<TILE_SIZE/2; y++) {
27     uint16_t * src_p = (uint16_t*)(src_arr->data + (2*y)*src_arr->strides[0]);
28     uint16_t * dst_p = (uint16_t*)(dst_arr->data + (y+dst_y)*dst_arr->strides[0]);
29     dst_p += 3*dst_x;
30     for(int x=0; x<TILE_SIZE/2; x++) {
31       dst_p[0] = src_p[0]/4 + (src_p+3)[0]/4 + (src_p+3*TILE_SIZE)[0]/4 + (src_p+3*TILE_SIZE+3)[0]/4;
32       dst_p[1] = src_p[1]/4 + (src_p+3)[1]/4 + (src_p+3*TILE_SIZE)[1]/4 + (src_p+3*TILE_SIZE+3)[1]/4;
33       dst_p[2] = src_p[2]/4 + (src_p+3)[2]/4 + (src_p+3*TILE_SIZE)[2]/4 + (src_p+3*TILE_SIZE+3)[2]/4;
34       src_p += 6;
35       dst_p += 3;
36     }
37   }
38 }
39 // downscale a tile to half its size using bilinear interpolation
40 // used mainly for generating tiledsurface mipmaps
41 void tile_downscale_rgba16(PyObject *src, PyObject *dst, int dst_x, int dst_y) {
42 #ifdef HEAVY_DEBUG
43   assert(PyArray_DIM(src, 0) == TILE_SIZE);
44   assert(PyArray_DIM(src, 1) == TILE_SIZE);
45   assert(PyArray_TYPE(src) == NPY_UINT16);
46   assert(PyArray_ISCARRAY(src));
47
48   assert(PyArray_DIM(dst, 0) == TILE_SIZE);
49   assert(PyArray_DIM(dst, 1) == TILE_SIZE);
50   assert(PyArray_TYPE(dst) == NPY_UINT16);
51   assert(PyArray_ISCARRAY(dst));
52 #endif
53
54   PyArrayObject* src_arr = ((PyArrayObject*)src);
55   PyArrayObject* dst_arr = ((PyArrayObject*)dst);
56
57   for (int y=0; y<TILE_SIZE/2; y++) {
58     uint16_t * src_p = (uint16_t*)(src_arr->data + (2*y)*src_arr->strides[0]);
59     uint16_t * dst_p = (uint16_t*)(dst_arr->data + (y+dst_y)*dst_arr->strides[0]);
60     dst_p += 4*dst_x;
61     for(int x=0; x<TILE_SIZE/2; x++) {
62       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;
63       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;
64       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;
65       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;
66       src_p += 8;
67       dst_p += 4;
68     }
69   }
70 }
71
72 void tile_composite_rgba16_over_rgb16(PyObject * src, PyObject * dst, float alpha) {
73 #ifdef HEAVY_DEBUG
74   assert(PyArray_DIM(src, 0) == TILE_SIZE);
75   assert(PyArray_DIM(src, 1) == TILE_SIZE);
76   assert(PyArray_DIM(src, 2) == 4);
77   assert(PyArray_TYPE(src) == NPY_UINT16);
78   assert(PyArray_ISCARRAY(src));
79
80   assert(PyArray_DIM(dst, 0) == TILE_SIZE);
81   assert(PyArray_DIM(dst, 1) == TILE_SIZE);
82   assert(PyArray_DIM(dst, 2) == 3);
83   assert(PyArray_TYPE(dst) == NPY_UINT16);
84   assert(PyArray_ISBEHAVED(dst));
85 #endif
86   
87   PyArrayObject* dst_arr = ((PyArrayObject*)dst);
88 #ifdef HEAVY_DEBUG
89   assert(dst_arr->strides[1] == 3*sizeof(uint16_t));
90   assert(dst_arr->strides[2] ==   sizeof(uint16_t));
91 #endif
92
93   uint32_t opac  = alpha * (1<<15) + 0.5;
94   opac = CLAMP(opac, 0, 1<<15);
95   if (opac == 0) return;
96
97   uint16_t * src_p  = (uint16_t*)((PyArrayObject*)src)->data;
98   char * p = dst_arr->data;
99   for (int y=0; y<TILE_SIZE; y++) {
100     uint16_t  * dst_p  = (uint16_t*) (p);
101     for (int x=0; x<TILE_SIZE; x++) {
102       // resultAlpha = 1.0 (thus it does not matter if resultColor is premultiplied alpha or not)
103       // resultColor = topColor + (1.0 - topAlpha) * bottomColor
104       const uint32_t one_minus_topAlpha = (1<<15) - src_p[3]*opac/(1<<15);
105       dst_p[0] = ((uint32_t)src_p[0]*opac + one_minus_topAlpha*dst_p[0]) / (1<<15);
106       dst_p[1] = ((uint32_t)src_p[1]*opac + one_minus_topAlpha*dst_p[1]) / (1<<15);
107       dst_p[2] = ((uint32_t)src_p[2]*opac + one_minus_topAlpha*dst_p[2]) / (1<<15);
108       src_p += 4;
109       dst_p += 3;
110     }
111     p += dst_arr->strides[0];
112   }
113 }
114
115 // used to copy the background before starting to composite over it
116 //
117 // simply array copying (numpy assignment operator is about 13 times slower, sadly)
118 // The above comment is true when the array is sliced; it's only about two
119 // times faster now, in the current usecae.
120 void tile_blit_rgb16_into_rgb16(PyObject * src, PyObject * dst) {
121   PyArrayObject* src_arr = ((PyArrayObject*)src);
122   PyArrayObject* dst_arr = ((PyArrayObject*)dst);
123
124 #ifdef HEAVY_DEBUG
125   assert(PyArray_DIM(dst, 0) == TILE_SIZE);
126   assert(PyArray_DIM(dst, 1) == TILE_SIZE);
127   assert(PyArray_DIM(dst, 2) == 3);
128   assert(PyArray_TYPE(dst) == NPY_UINT16);
129   assert(PyArray_ISCARRAY(dst));
130   assert(dst_arr->strides[1] == 3*sizeof(uint16_t));
131   assert(dst_arr->strides[2] ==   sizeof(uint16_t));
132
133   assert(PyArray_DIM(src, 0) == TILE_SIZE);
134   assert(PyArray_DIM(src, 1) == TILE_SIZE);
135   assert(PyArray_DIM(src, 2) == 3);
136   assert(PyArray_TYPE(src) == NPY_UINT16);
137   assert(PyArray_ISCARRAY(dst));
138   assert(src_arr->strides[1] == 3*sizeof(uint16_t));
139   assert(src_arr->strides[2] ==   sizeof(uint16_t));
140 #endif
141
142   memcpy(dst_arr->data, src_arr->data, TILE_SIZE*TILE_SIZE*3*sizeof(uint16_t));
143   /* the code below can be used if it is not ISCARRAY, but only ISBEHAVED:
144   char * src_p = src_arr->data;
145   char * dst_p = dst_arr->data;
146   for (int y=0; y<TILE_SIZE; y++) {
147     memcpy(dst_p, src_p, TILE_SIZE*3);
148     src_p += src_arr->strides[0];
149     dst_p += dst_arr->strides[0];
150   }
151   */
152 }
153
154 // used mainly for saving layers (transparent PNG)
155 void tile_convert_rgba16_to_rgba8(PyObject * src, PyObject * dst) {
156   PyArrayObject* src_arr = ((PyArrayObject*)src);
157   PyArrayObject* dst_arr = ((PyArrayObject*)dst);
158
159 #ifdef HEAVY_DEBUG
160   assert(PyArray_DIM(dst, 0) == TILE_SIZE);
161   assert(PyArray_DIM(dst, 1) == TILE_SIZE);
162   assert(PyArray_DIM(dst, 2) == 4);
163   assert(PyArray_TYPE(dst) == NPY_UINT8);
164   assert(PyArray_ISBEHAVED(dst));
165   assert(dst_arr->strides[1] == 4*sizeof(uint8_t));
166   assert(dst_arr->strides[2] ==   sizeof(uint8_t));
167
168   assert(PyArray_DIM(src, 0) == TILE_SIZE);
169   assert(PyArray_DIM(src, 1) == TILE_SIZE);
170   assert(PyArray_DIM(src, 2) == 4);
171   assert(PyArray_TYPE(src) == NPY_UINT16);
172   assert(PyArray_ISBEHAVED(src));
173   assert(src_arr->strides[1] == 4*sizeof(uint16_t));
174   assert(src_arr->strides[2] ==   sizeof(uint16_t));
175 #endif
176
177   for (int y=0; y<TILE_SIZE; y++) {
178     uint16_t * src_p = (uint16_t*)(src_arr->data + y*src_arr->strides[0]);
179     uint8_t  * dst_p = (uint8_t*)(dst_arr->data + y*dst_arr->strides[0]);
180     for (int x=0; x<TILE_SIZE; x++) {
181       uint32_t r, g, b, a;
182       r = *src_p++;
183       g = *src_p++;
184       b = *src_p++;
185       a = *src_p++;
186 #ifdef HEAVY_DEBUG
187       assert(a<=(1<<15));
188       assert(r<=(1<<15));
189       assert(g<=(1<<15));
190       assert(b<=(1<<15));
191       assert(r<=a);
192       assert(g<=a);
193       assert(b<=a);
194 #endif
195       // un-premultiply alpha (with rounding)
196       if (a != 0) {
197         r = ((r << 15) + a/2) / a;
198         g = ((g << 15) + a/2) / a;
199         b = ((b << 15) + a/2) / a;
200       } else {
201         r = g = b = 0;
202       }
203 #ifdef HEAVY_DEBUG
204       assert(a<=(1<<15));
205       assert(r<=(1<<15));
206       assert(g<=(1<<15));
207       assert(b<=(1<<15));
208 #endif
209
210       /*
211       // Variant A) rounding
212       const uint32_t add_r = (1<<15)/2;
213       const uint32_t add_g = (1<<15)/2;
214       const uint32_t add_b = (1<<15)/2;
215       const uint32_t add_a = (1<<15)/2;
216       */
217       
218       /*
219       // Variant B) naive dithering
220       // This can alter the alpha channel during a load->save cycle.
221       const uint32_t add_r = rand() % (1<<15);
222       const uint32_t add_g = rand() % (1<<15);
223       const uint32_t add_b = rand() % (1<<15);
224       const uint32_t add_a = rand() % (1<<15);
225       */
226
227       // Variant C) slightly better dithering
228       // make sure we don't dither rounding errors (those did occur when converting 8bit-->16bit)
229       // this preserves the alpha channel, but we still add noise to the highly transparent colors
230       // OPTIMIZE: calling rand() slows us down...
231       const uint32_t add_r = (rand() % (1<<15)) * 240/256 + (1<<15) * 8/256;
232       const uint32_t add_g = add_r; // hm... do not produce too much color noise
233       const uint32_t add_b = add_r;
234       const uint32_t add_a = (rand() % (1<<15)) * 240/256 + (1<<15) * 8/256;
235       // TODO: error diffusion might work better than random dithering...
236
237 #ifdef HEAVY_DEBUG
238       assert(add_a < (1<<15));
239       assert(add_a >= 0);
240 #endif
241
242       *dst_p++ = (r * 255 + add_r) / (1<<15);
243       *dst_p++ = (g * 255 + add_g) / (1<<15);
244       *dst_p++ = (b * 255 + add_b) / (1<<15);
245       *dst_p++ = (a * 255 + add_a) / (1<<15);
246     }
247     src_p += src_arr->strides[0];
248     dst_p += dst_arr->strides[0];
249   }
250 }
251
252 void tile_clear(PyObject * dst) {
253   PyArrayObject* dst_arr = ((PyArrayObject*)dst);
254
255 #ifdef HEAVY_DEBUG
256   assert(PyArray_DIM(dst, 0) == TILE_SIZE);
257   assert(PyArray_DIM(dst, 1) == TILE_SIZE);
258   assert(PyArray_TYPE(dst) == NPY_UINT8);
259   assert(PyArray_ISBEHAVED(dst));
260   assert(dst_arr->strides[1] <= 8);
261 #endif
262
263   for (int y=0; y<TILE_SIZE; y++) {
264     uint8_t  * dst_p = (uint8_t*)(dst_arr->data + y*dst_arr->strides[0]);
265     memset(dst_p, 0, TILE_SIZE*dst_arr->strides[1]);
266     dst_p += dst_arr->strides[0];
267   }
268 }
269
270 // used after compositing
271 void tile_convert_rgb16_to_rgb8(PyObject * src, PyObject * dst) {
272   PyArrayObject* src_arr = ((PyArrayObject*)src);
273   PyArrayObject* dst_arr = ((PyArrayObject*)dst);
274
275 #ifdef HEAVY_DEBUG
276   assert(PyArray_DIM(dst, 0) == TILE_SIZE);
277   assert(PyArray_DIM(dst, 1) == TILE_SIZE);
278   assert(PyArray_TYPE(dst) == NPY_UINT8);
279   assert(PyArray_ISBEHAVED(dst));
280   assert(dst_arr->strides[1] == PyArray_DIM(dst, 2)*sizeof(uint8_t));
281   assert(dst_arr->strides[2] ==   sizeof(uint8_t));
282
283   assert(PyArray_DIM(src, 0) == TILE_SIZE);
284   assert(PyArray_DIM(src, 1) == TILE_SIZE);
285   assert(PyArray_DIM(src, 2) == 3);
286   assert(PyArray_TYPE(src) == NPY_UINT16);
287   assert(PyArray_ISBEHAVED(src));
288   assert(src_arr->strides[1] == 3*sizeof(uint16_t));
289   assert(src_arr->strides[2] ==   sizeof(uint16_t));
290 #endif
291
292   bool dst_has_alpha = PyArray_DIM(dst, 2) == 4;
293
294   for (int y=0; y<TILE_SIZE; y++) {
295     uint16_t * src_p = (uint16_t*)(src_arr->data + y*src_arr->strides[0]);
296     uint8_t  * dst_p = (uint8_t*)(dst_arr->data + y*dst_arr->strides[0]);
297     if (dst_has_alpha) {
298       for (int x=0; x<TILE_SIZE; x++) {
299         uint32_t r, g, b;
300         r = *src_p++;
301         g = *src_p++;
302         b = *src_p++;
303 #ifdef HEAVY_DEBUG
304         assert(r<=(1<<15));
305         assert(g<=(1<<15));
306         assert(b<=(1<<15));
307 #endif
308           
309         // Doing rounding for now.
310         // TODO: error diffusion / dithering? (but watch the performance benchmarks)
311         const uint32_t add = (1<<15)/2;
312           
313         *dst_p++ = (r * 255 + add) / (1<<15);
314         *dst_p++ = (g * 255 + add) / (1<<15);
315         *dst_p++ = (b * 255 + add) / (1<<15);
316         *dst_p++ = 255;
317       }
318     } else {
319       for (int x=0; x<TILE_SIZE; x++) {
320         uint32_t r, g, b;
321         r = *src_p++;
322         g = *src_p++;
323         b = *src_p++;
324 #ifdef HEAVY_DEBUG
325         assert(r<=(1<<15));
326         assert(g<=(1<<15));
327         assert(b<=(1<<15));
328 #endif
329           
330         // Doing rounding for now.
331         // TODO: error diffusion / dithering? (but watch the performance benchmarks)
332         const uint32_t add = (1<<15)/2;
333           
334         *dst_p++ = (r * 255 + add) / (1<<15);
335         *dst_p++ = (g * 255 + add) / (1<<15);
336         *dst_p++ = (b * 255 + add) / (1<<15);
337       }
338     }
339     src_p += src_arr->strides[0];
340     dst_p += dst_arr->strides[0];
341   }
342 }
343
344 // used mainly for loading layers (transparent PNG)
345 void tile_convert_rgba8_to_rgba16(PyObject * src, PyObject * dst) {
346   PyArrayObject* src_arr = ((PyArrayObject*)src);
347   PyArrayObject* dst_arr = ((PyArrayObject*)dst);
348
349 #ifdef HEAVY_DEBUG
350   assert(PyArray_DIM(dst, 0) == TILE_SIZE);
351   assert(PyArray_DIM(dst, 1) == TILE_SIZE);
352   assert(PyArray_DIM(dst, 2) == 4);
353   assert(PyArray_TYPE(dst) == NPY_UINT16);
354   assert(PyArray_ISBEHAVED(dst));
355   assert(dst_arr->strides[1] == 4*sizeof(uint16_t));
356   assert(dst_arr->strides[2] ==   sizeof(uint16_t));
357
358   assert(PyArray_DIM(src, 0) == TILE_SIZE);
359   assert(PyArray_DIM(src, 1) == TILE_SIZE);
360   assert(PyArray_DIM(src, 2) == 4);
361   assert(PyArray_TYPE(src) == NPY_UINT8);
362   assert(PyArray_ISBEHAVED(src));
363   assert(src_arr->strides[1] == 4*sizeof(uint8_t));
364   assert(src_arr->strides[2] ==   sizeof(uint8_t));
365 #endif
366
367   for (int y=0; y<TILE_SIZE; y++) {
368     uint8_t  * src_p = (uint8_t*)(src_arr->data + y*src_arr->strides[0]);
369     uint16_t * dst_p = (uint16_t*)(dst_arr->data + y*dst_arr->strides[0]);
370     for (int x=0; x<TILE_SIZE; x++) {
371       uint32_t r, g, b, a;
372       r = *src_p++;
373       g = *src_p++;
374       b = *src_p++;
375       a = *src_p++;
376
377       // convert to fixed point (with rounding)
378       r = (r * (1<<15) + 255/2) / 255;
379       g = (g * (1<<15) + 255/2) / 255;
380       b = (b * (1<<15) + 255/2) / 255;
381       a = (a * (1<<15) + 255/2) / 255;
382
383       // premultiply alpha (with rounding), save back
384       *dst_p++ = (r * a + (1<<15)/2) / (1<<15);
385       *dst_p++ = (g * a + (1<<15)/2) / (1<<15);
386       *dst_p++ = (b * a + (1<<15)/2) / (1<<15);
387       *dst_p++ = a;
388     }
389   }
390 }