1 /* This file is part of MyPaint.
2 * Copyright (C) 2008-2009 by Martin Renold <martinxyz@gmx.ch>
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.
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) {
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));
19 assert(PyArray_TYPE(dst) == NPY_UINT16);
20 assert(PyArray_ISCARRAY(dst));
23 PyArrayObject* src_arr = ((PyArrayObject*)src);
24 PyArrayObject* dst_arr = ((PyArrayObject*)dst);
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]);
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;
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) {
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));
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));
54 PyArrayObject* src_arr = ((PyArrayObject*)src);
55 PyArrayObject* dst_arr = ((PyArrayObject*)dst);
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]);
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;
72 void tile_composite_rgba16_over_rgb16(PyObject * src, PyObject * dst, float alpha) {
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));
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));
87 PyArrayObject* dst_arr = ((PyArrayObject*)dst);
89 assert(dst_arr->strides[1] == 3*sizeof(uint16_t));
90 assert(dst_arr->strides[2] == sizeof(uint16_t));
93 uint32_t opac = alpha * (1<<15) + 0.5;
94 opac = CLAMP(opac, 0, 1<<15);
95 if (opac == 0) return;
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);
111 p += dst_arr->strides[0];
115 // used to copy the background before starting to composite over it
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);
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));
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));
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];
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);
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));
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));
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++) {
195 // un-premultiply alpha (with rounding)
197 r = ((r << 15) + a/2) / a;
198 g = ((g << 15) + a/2) / a;
199 b = ((b << 15) + a/2) / a;
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;
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);
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...
238 assert(add_a < (1<<15));
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);
247 src_p += src_arr->strides[0];
248 dst_p += dst_arr->strides[0];
252 void tile_clear(PyObject * dst) {
253 PyArrayObject* dst_arr = ((PyArrayObject*)dst);
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);
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];
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);
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));
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));
292 bool dst_has_alpha = PyArray_DIM(dst, 2) == 4;
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]);
298 for (int x=0; x<TILE_SIZE; x++) {
309 // Doing rounding for now.
310 // TODO: error diffusion / dithering? (but watch the performance benchmarks)
311 const uint32_t add = (1<<15)/2;
313 *dst_p++ = (r * 255 + add) / (1<<15);
314 *dst_p++ = (g * 255 + add) / (1<<15);
315 *dst_p++ = (b * 255 + add) / (1<<15);
319 for (int x=0; x<TILE_SIZE; x++) {
330 // Doing rounding for now.
331 // TODO: error diffusion / dithering? (but watch the performance benchmarks)
332 const uint32_t add = (1<<15)/2;
334 *dst_p++ = (r * 255 + add) / (1<<15);
335 *dst_p++ = (g * 255 + add) / (1<<15);
336 *dst_p++ = (b * 255 + add) / (1<<15);
339 src_p += src_arr->strides[0];
340 dst_p += dst_arr->strides[0];
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);
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));
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));
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++) {
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;
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);