/* This file is part of MyPaint. * Copyright (C) 2008-2009 by Martin Renold * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ // downscale a tile to half its size using bilinear interpolation // used mainly for generating background mipmaps void tile_downscale_rgb16(PyObject *src, PyObject *dst, int dst_x, int dst_y) { #ifdef HEAVY_DEBUG assert(PyArray_DIM(src, 0) == TILE_SIZE); assert(PyArray_DIM(src, 1) == TILE_SIZE); assert(PyArray_TYPE(src) == NPY_UINT16); assert(PyArray_ISCARRAY(src)); assert(PyArray_TYPE(dst) == NPY_UINT16); assert(PyArray_ISCARRAY(dst)); #endif PyArrayObject* src_arr = ((PyArrayObject*)src); PyArrayObject* dst_arr = ((PyArrayObject*)dst); for (int y=0; ydata + (2*y)*src_arr->strides[0]); uint16_t * dst_p = (uint16_t*)(dst_arr->data + (y+dst_y)*dst_arr->strides[0]); dst_p += 3*dst_x; for(int x=0; xdata + (2*y)*src_arr->strides[0]); uint16_t * dst_p = (uint16_t*)(dst_arr->data + (y+dst_y)*dst_arr->strides[0]); dst_p += 4*dst_x; for(int x=0; xstrides[1] == 3*sizeof(uint16_t)); assert(dst_arr->strides[2] == sizeof(uint16_t)); #endif uint32_t opac = alpha * (1<<15) + 0.5; opac = CLAMP(opac, 0, 1<<15); if (opac == 0) return; uint16_t * src_p = (uint16_t*)((PyArrayObject*)src)->data; char * p = dst_arr->data; for (int y=0; ystrides[0]; } } // used to copy the background before starting to composite over it // // simply array copying (numpy assignment operator is about 13 times slower, sadly) // The above comment is true when the array is sliced; it's only about two // times faster now, in the current usecae. void tile_blit_rgb16_into_rgb16(PyObject * src, PyObject * dst) { PyArrayObject* src_arr = ((PyArrayObject*)src); PyArrayObject* dst_arr = ((PyArrayObject*)dst); #ifdef HEAVY_DEBUG assert(PyArray_DIM(dst, 0) == TILE_SIZE); assert(PyArray_DIM(dst, 1) == TILE_SIZE); assert(PyArray_DIM(dst, 2) == 3); assert(PyArray_TYPE(dst) == NPY_UINT16); assert(PyArray_ISCARRAY(dst)); assert(dst_arr->strides[1] == 3*sizeof(uint16_t)); assert(dst_arr->strides[2] == sizeof(uint16_t)); assert(PyArray_DIM(src, 0) == TILE_SIZE); assert(PyArray_DIM(src, 1) == TILE_SIZE); assert(PyArray_DIM(src, 2) == 3); assert(PyArray_TYPE(src) == NPY_UINT16); assert(PyArray_ISCARRAY(dst)); assert(src_arr->strides[1] == 3*sizeof(uint16_t)); assert(src_arr->strides[2] == sizeof(uint16_t)); #endif memcpy(dst_arr->data, src_arr->data, TILE_SIZE*TILE_SIZE*3*sizeof(uint16_t)); /* the code below can be used if it is not ISCARRAY, but only ISBEHAVED: char * src_p = src_arr->data; char * dst_p = dst_arr->data; for (int y=0; ystrides[0]; dst_p += dst_arr->strides[0]; } */ } // used mainly for saving layers (transparent PNG) void tile_convert_rgba16_to_rgba8(PyObject * src, PyObject * dst) { PyArrayObject* src_arr = ((PyArrayObject*)src); PyArrayObject* dst_arr = ((PyArrayObject*)dst); #ifdef HEAVY_DEBUG assert(PyArray_DIM(dst, 0) == TILE_SIZE); assert(PyArray_DIM(dst, 1) == TILE_SIZE); assert(PyArray_DIM(dst, 2) == 4); assert(PyArray_TYPE(dst) == NPY_UINT8); assert(PyArray_ISBEHAVED(dst)); assert(dst_arr->strides[1] == 4*sizeof(uint8_t)); assert(dst_arr->strides[2] == sizeof(uint8_t)); assert(PyArray_DIM(src, 0) == TILE_SIZE); assert(PyArray_DIM(src, 1) == TILE_SIZE); assert(PyArray_DIM(src, 2) == 4); assert(PyArray_TYPE(src) == NPY_UINT16); assert(PyArray_ISBEHAVED(src)); assert(src_arr->strides[1] == 4*sizeof(uint16_t)); assert(src_arr->strides[2] == sizeof(uint16_t)); #endif for (int y=0; ydata + y*src_arr->strides[0]); uint8_t * dst_p = (uint8_t*)(dst_arr->data + y*dst_arr->strides[0]); for (int x=0; xsave cycle. const uint32_t add_r = rand() % (1<<15); const uint32_t add_g = rand() % (1<<15); const uint32_t add_b = rand() % (1<<15); const uint32_t add_a = rand() % (1<<15); */ // Variant C) slightly better dithering // make sure we don't dither rounding errors (those did occur when converting 8bit-->16bit) // this preserves the alpha channel, but we still add noise to the highly transparent colors // OPTIMIZE: calling rand() slows us down... const uint32_t add_r = (rand() % (1<<15)) * 240/256 + (1<<15) * 8/256; const uint32_t add_g = add_r; // hm... do not produce too much color noise const uint32_t add_b = add_r; const uint32_t add_a = (rand() % (1<<15)) * 240/256 + (1<<15) * 8/256; // TODO: error diffusion might work better than random dithering... #ifdef HEAVY_DEBUG assert(add_a < (1<<15)); assert(add_a >= 0); #endif *dst_p++ = (r * 255 + add_r) / (1<<15); *dst_p++ = (g * 255 + add_g) / (1<<15); *dst_p++ = (b * 255 + add_b) / (1<<15); *dst_p++ = (a * 255 + add_a) / (1<<15); } src_p += src_arr->strides[0]; dst_p += dst_arr->strides[0]; } } void tile_clear(PyObject * dst) { PyArrayObject* dst_arr = ((PyArrayObject*)dst); #ifdef HEAVY_DEBUG assert(PyArray_DIM(dst, 0) == TILE_SIZE); assert(PyArray_DIM(dst, 1) == TILE_SIZE); assert(PyArray_TYPE(dst) == NPY_UINT8); assert(PyArray_ISBEHAVED(dst)); assert(dst_arr->strides[1] <= 8); #endif for (int y=0; ydata + y*dst_arr->strides[0]); memset(dst_p, 0, TILE_SIZE*dst_arr->strides[1]); dst_p += dst_arr->strides[0]; } } // used after compositing void tile_convert_rgb16_to_rgb8(PyObject * src, PyObject * dst) { PyArrayObject* src_arr = ((PyArrayObject*)src); PyArrayObject* dst_arr = ((PyArrayObject*)dst); #ifdef HEAVY_DEBUG assert(PyArray_DIM(dst, 0) == TILE_SIZE); assert(PyArray_DIM(dst, 1) == TILE_SIZE); assert(PyArray_TYPE(dst) == NPY_UINT8); assert(PyArray_ISBEHAVED(dst)); assert(dst_arr->strides[1] == PyArray_DIM(dst, 2)*sizeof(uint8_t)); assert(dst_arr->strides[2] == sizeof(uint8_t)); assert(PyArray_DIM(src, 0) == TILE_SIZE); assert(PyArray_DIM(src, 1) == TILE_SIZE); assert(PyArray_DIM(src, 2) == 3); assert(PyArray_TYPE(src) == NPY_UINT16); assert(PyArray_ISBEHAVED(src)); assert(src_arr->strides[1] == 3*sizeof(uint16_t)); assert(src_arr->strides[2] == sizeof(uint16_t)); #endif bool dst_has_alpha = PyArray_DIM(dst, 2) == 4; for (int y=0; ydata + y*src_arr->strides[0]); uint8_t * dst_p = (uint8_t*)(dst_arr->data + y*dst_arr->strides[0]); if (dst_has_alpha) { for (int x=0; xstrides[0]; dst_p += dst_arr->strides[0]; } } // used mainly for loading layers (transparent PNG) void tile_convert_rgba8_to_rgba16(PyObject * src, PyObject * dst) { PyArrayObject* src_arr = ((PyArrayObject*)src); PyArrayObject* dst_arr = ((PyArrayObject*)dst); #ifdef HEAVY_DEBUG assert(PyArray_DIM(dst, 0) == TILE_SIZE); assert(PyArray_DIM(dst, 1) == TILE_SIZE); assert(PyArray_DIM(dst, 2) == 4); assert(PyArray_TYPE(dst) == NPY_UINT16); assert(PyArray_ISBEHAVED(dst)); assert(dst_arr->strides[1] == 4*sizeof(uint16_t)); assert(dst_arr->strides[2] == sizeof(uint16_t)); assert(PyArray_DIM(src, 0) == TILE_SIZE); assert(PyArray_DIM(src, 1) == TILE_SIZE); assert(PyArray_DIM(src, 2) == 4); assert(PyArray_TYPE(src) == NPY_UINT8); assert(PyArray_ISBEHAVED(src)); assert(src_arr->strides[1] == 4*sizeof(uint8_t)); assert(src_arr->strides[2] == sizeof(uint8_t)); #endif for (int y=0; ydata + y*src_arr->strides[0]); uint16_t * dst_p = (uint16_t*)(dst_arr->data + y*dst_arr->strides[0]); for (int x=0; x