static int HALF_setitem(PyObject *op, char *ov, PyArrayObject *ap) { npy_half temp; /* ensures alignment */ if (PyArray_IsScalar(op, Half)) { temp = ((PyHalfScalarObject *)op)->obval; } else { temp = MyPyFloat_AsHalf(op); } if (PyErr_Occurred()) { if (PySequence_Check(op)) { PyErr_Clear(); PyErr_SetString(PyExc_ValueError, "setting an array element with a sequence."); } return -1; } if (ap == NULL || PyArray_ISBEHAVED(ap)) *((npy_half *)ov)=temp; else { ap->descr->f->copyswap(ov, &temp, !PyArray_ISNOTSWAPPED(ap), ap); } return 0; }
static int QUATERNION_setitem(PyObject *op, char *ov, PyArrayObject *ap) { quaternion q; if (PyArray_IsScalar(op, Quaternion)) { q = ((PyQuaternionScalarObject *)op)->obval; } else { q.w = PyFloat_AsDouble(PyTuple_GetItem(op, 0)); q.x = PyFloat_AsDouble(PyTuple_GetItem(op, 1)); q.y = PyFloat_AsDouble(PyTuple_GetItem(op, 2)); q.z = PyFloat_AsDouble(PyTuple_GetItem(op, 3)); } if (PyErr_Occurred()) { if (PySequence_Check(op)) { PyErr_Clear(); PyErr_SetString(PyExc_ValueError, "setting an array element with a sequence."); } return -1; } if (ap == NULL || PyArray_ISBEHAVED(ap)) *((quaternion *)ov)=q; else { PyArray_Descr *descr; descr = PyArray_DescrFromType(NPY_DOUBLE); descr->f->copyswap(ov, &q.w, !PyArray_ISNOTSWAPPED(ap), NULL); descr->f->copyswap(ov+8, &q.x, !PyArray_ISNOTSWAPPED(ap), NULL); descr->f->copyswap(ov+16, &q.y, !PyArray_ISNOTSWAPPED(ap), NULL); descr->f->copyswap(ov+24, &q.z, !PyArray_ISNOTSWAPPED(ap), NULL); Py_DECREF(descr); } return 0; }
PyObject * load_png_fast_progressive (char *filename, PyObject *get_buffer_callback) { // Note: we are not using the method that libpng calls "Reading PNG // files progressively". That method would involve feeding the data // into libpng piece by piece, which is not necessary if we can give // libpng a simple FILE pointer. png_structp png_ptr = NULL; png_infop info_ptr = NULL; PyObject * result = NULL; FILE *fp = NULL; uint32_t width, height; uint32_t rows_left; png_byte color_type; png_byte bit_depth; bool have_alpha; char *cm_processing = NULL; // ICC profile-based colour conversion data. png_charp icc_profile_name = NULL; int icc_compression_type = 0; #if PNG_LIBPNG_VER < 10500 // 1.5.0beta36, according to libpng CHANGES png_charp icc_profile = NULL; #else png_bytep icc_profile = NULL; #endif png_uint_32 icc_proflen = 0; // The sRGB flag has an intent field, which we ignore - // the target gamut is sRGB already. int srgb_intent = 0; // Generic RGB space conversion params. // The assumptions we're making are those of sRGB, // but they'll be overridden by gammas or primaries in the file if used. bool generic_rgb_have_gAMA = false; bool generic_rgb_have_cHRM = false; double generic_rgb_file_gamma = 45455 / PNG_gAMA_scale; double generic_rgb_white_x = 31270 / PNG_cHRM_scale; double generic_rgb_white_y = 32900 / PNG_cHRM_scale; double generic_rgb_red_x = 64000 / PNG_cHRM_scale; double generic_rgb_red_y = 33000 / PNG_cHRM_scale; double generic_rgb_green_x = 30000 / PNG_cHRM_scale; double generic_rgb_green_y = 60000 / PNG_cHRM_scale; double generic_rgb_blue_x = 15000 / PNG_cHRM_scale; double generic_rgb_blue_y = 6000 / PNG_cHRM_scale; // Indicates the case where no CM information was present in the file and we // treated it as sRGB. bool possible_legacy_png = false; // LCMS stuff cmsHPROFILE input_buffer_profile = NULL; cmsHPROFILE nparray_data_profile = cmsCreate_sRGBProfile(); cmsHTRANSFORM input_buffer_to_nparray = NULL; cmsToneCurve *gamma_transfer_func = NULL; cmsUInt32Number input_buffer_format = 0; cmsSetLogErrorHandler(log_lcms2_error); fp = fopen(filename, "rb"); if (!fp) { PyErr_SetFromErrno(PyExc_IOError); //PyErr_Format(PyExc_IOError, "Could not open PNG file for writing: %s", // filename); goto cleanup; } png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, (png_voidp)NULL, png_read_error_callback, NULL); if (!png_ptr) { PyErr_SetString(PyExc_MemoryError, "png_create_write_struct() failed"); goto cleanup; } info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { PyErr_SetString(PyExc_MemoryError, "png_create_info_struct() failed"); goto cleanup; } if (setjmp(png_jmpbuf(png_ptr))) { goto cleanup; } png_init_io(png_ptr, fp); png_read_info(png_ptr, info_ptr); // If there's an embedded ICC profile, use it in preference to any other // colour management information present. if (png_get_iCCP (png_ptr, info_ptr, &icc_profile_name, &icc_compression_type, &icc_profile, &icc_proflen)) { input_buffer_profile = cmsOpenProfileFromMem(icc_profile, icc_proflen); if (! input_buffer_profile) { PyErr_SetString(PyExc_MemoryError, "cmsOpenProfileFromMem() failed"); goto cleanup; } cm_processing = "iCCP (use embedded colour profile)"; } // Shorthand for sRGB. else if (png_get_sRGB (png_ptr, info_ptr, &srgb_intent)) { input_buffer_profile = cmsCreate_sRGBProfile(); cm_processing = "sRGB (explicit sRGB chunk)"; } else { // We might have generic RGB transformation information in the form of // the chromaticities for R, G and B and a generic gamma curve. if (png_get_cHRM (png_ptr, info_ptr, &generic_rgb_white_x, &generic_rgb_white_y, &generic_rgb_red_x, &generic_rgb_red_y, &generic_rgb_green_x, &generic_rgb_green_y, &generic_rgb_blue_x, &generic_rgb_blue_y)) { generic_rgb_have_cHRM = true; } if (png_get_gAMA(png_ptr, info_ptr, &generic_rgb_file_gamma)) { generic_rgb_have_gAMA = true; } if (generic_rgb_have_gAMA || generic_rgb_have_cHRM) { cmsCIExyYTRIPLE primaries = {{generic_rgb_red_x, generic_rgb_red_y}, {generic_rgb_green_x, generic_rgb_green_y}, {generic_rgb_blue_x, generic_rgb_blue_y}}; cmsCIExyY white_point = {generic_rgb_white_x, generic_rgb_white_y}; gamma_transfer_func = cmsBuildGamma(NULL, 1.0/generic_rgb_file_gamma); cmsToneCurve *transfer_funcs[3] = {gamma_transfer_func, gamma_transfer_func, gamma_transfer_func }; input_buffer_profile = cmsCreateRGBProfile(&white_point, &primaries, transfer_funcs); cm_processing = "cHRM and/or gAMA (generic RGB space)"; } // Possible legacy PNG, or rather one which might have been written with an // old version of MyPaint. Treat as sRGB, but flag the strangeness because // it might be important for PNGs in old OpenRaster files. else { possible_legacy_png = true; input_buffer_profile = cmsCreate_sRGBProfile(); cm_processing = "sRGB (no CM chunks present)"; } } if (png_get_interlace_type (png_ptr, info_ptr) != PNG_INTERLACE_NONE) { PyErr_SetString(PyExc_RuntimeError, "Interlaced PNG files are not supported!"); goto cleanup; } color_type = png_get_color_type(png_ptr, info_ptr); bit_depth = png_get_bit_depth(png_ptr, info_ptr); have_alpha = color_type & PNG_COLOR_MASK_ALPHA; if (color_type == PNG_COLOR_TYPE_PALETTE) { png_set_palette_to_rgb(png_ptr); } if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) { png_set_expand_gray_1_2_4_to_8(png_ptr); } if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { png_set_tRNS_to_alpha(png_ptr); have_alpha = true; } if (bit_depth < 8) { png_set_packing(png_ptr); } if (!have_alpha) { png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_AFTER); } if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { png_set_gray_to_rgb(png_ptr); } png_read_update_info(png_ptr, info_ptr); // Verify what we have done bit_depth = png_get_bit_depth(png_ptr, info_ptr); if (! (bit_depth == 8 || bit_depth == 16)) { PyErr_SetString(PyExc_RuntimeError, "Failed to convince libpng to convert " "to 8 or 16 bits per channel"); goto cleanup; } if (png_get_color_type(png_ptr, info_ptr) != PNG_COLOR_TYPE_RGB_ALPHA) { PyErr_SetString(PyExc_RuntimeError, "Failed to convince libpng to convert " "to RGBA (wrong color_type)"); goto cleanup; } if (png_get_channels(png_ptr, info_ptr) != 4) { PyErr_SetString(PyExc_RuntimeError, "Failed to convince libpng to convert " "to RGBA (wrong number of channels)"); goto cleanup; } // PNGs use network byte order, i.e. big-endian in descending order // of bit significance. LittleCMS uses whatever's detected for the compiler. // ref: http://www.w3.org/TR/2003/REC-PNG-20031110/#7Integers-and-byte-order if (bit_depth == 16) { #ifdef CMS_USE_BIG_ENDIAN input_buffer_format = TYPE_RGBA_16; #else input_buffer_format = TYPE_RGBA_16_SE; #endif } else { input_buffer_format = TYPE_RGBA_8; } input_buffer_to_nparray = cmsCreateTransform (input_buffer_profile, input_buffer_format, nparray_data_profile, TYPE_RGBA_8, INTENT_PERCEPTUAL, 0); width = png_get_image_width(png_ptr, info_ptr); height = png_get_image_height(png_ptr, info_ptr); rows_left = height; while (rows_left) { PyObject *pyarr = NULL; uint32_t rows = 0; uint32_t row = 0; const uint8_t input_buf_bytes_per_pixel = (bit_depth==8) ? 4 : 8; const uint32_t input_buf_row_stride = sizeof(png_byte) * width * input_buf_bytes_per_pixel; png_byte *input_buffer = NULL; png_bytep *input_buf_row_pointers = NULL; pyarr = PyObject_CallFunction(get_buffer_callback, "ii", width, height); if (! pyarr) { PyErr_Format(PyExc_RuntimeError, "Get-buffer callback failed"); goto cleanup; } #ifdef HEAVY_DEBUG //assert(PyArray_ISCARRAY(arr)); assert(PyArray_NDIM(pyarr) == 3); assert(PyArray_DIM(pyarr, 1) == width); assert(PyArray_DIM(pyarr, 2) == 4); assert(PyArray_TYPE(pyarr) == NPY_UINT8); assert(PyArray_ISBEHAVED(pyarr)); assert(PyArray_STRIDE(pyarr, 1) == 4*sizeof(uint8_t)); assert(PyArray_STRIDE(pyarr, 2) == sizeof(uint8_t)); #endif rows = PyArray_DIM(pyarr, 0); if (rows > rows_left) { PyErr_Format(PyExc_RuntimeError, "Attempt to read %d rows from the PNG, " "but only %d are left", rows, rows_left); goto cleanup; } input_buffer = (png_byte *) malloc(rows * input_buf_row_stride); input_buf_row_pointers = (png_bytep *)malloc(rows * sizeof(png_bytep)); for (row=0; row<rows; row++) { input_buf_row_pointers[row] = input_buffer + (row * input_buf_row_stride); } png_read_rows(png_ptr, input_buf_row_pointers, NULL, rows); rows_left -= rows; for (row=0; row<rows; row++) { uint8_t *pyarr_row = (uint8_t *)PyArray_DATA(pyarr) + row*PyArray_STRIDE(pyarr, 0); uint8_t *input_row = input_buf_row_pointers[row]; // Really minimal fake colour management. Just remaps to sRGB. cmsDoTransform(input_buffer_to_nparray, input_row, pyarr_row, width); // lcms2 ignores alpha, so copy that verbatim // If it's 8bpc RGBA, use A. // If it's 16bpc RrGgBbAa, use A. for (uint32_t i=0; i<width; ++i) { const uint32_t pyarr_alpha_byte = (i*4) + 3; const uint32_t buf_alpha_byte = (i*input_buf_bytes_per_pixel) + ((bit_depth==8) ? 3 : 6); pyarr_row[pyarr_alpha_byte] = input_row[buf_alpha_byte]; } } free(input_buf_row_pointers); free(input_buffer); Py_DECREF(pyarr); } png_read_end(png_ptr, NULL); result = Py_BuildValue("{s:b,s:i,s:i,s:s}", "possible_legacy_png", possible_legacy_png, "width", width, "height", height, "cm_conversions_applied", cm_processing); cleanup: if (info_ptr) png_destroy_read_struct (&png_ptr, &info_ptr, NULL); // libpng's style is to free internally allocated stuff like the icc // tables in png_destroy_*(). I think. if (fp) fclose(fp); if (input_buffer_profile) cmsCloseProfile(input_buffer_profile); if (nparray_data_profile) cmsCloseProfile(nparray_data_profile); if (input_buffer_to_nparray) cmsDeleteTransform(input_buffer_to_nparray); if (gamma_transfer_func) cmsFreeToneCurve(gamma_transfer_func); return result; }
static PyObject * cKeo_linear_interpolate(PyObject *self, PyObject *args){ PyObject *keo_arr, *data_list; int strip_width,max_gap; npy_intp keo_arr_dims[2], data_list_dims[1]; int keo_arr_num_dim, data_list_num_dim; long int x,y,k; int start_pix, end_pix, offset; double gradient; //parse the arguments passed to the function by Python - no increase in ref count if(!PyArg_ParseTuple(args, "OOii", &keo_arr, &data_list, &strip_width, &max_gap)){ //no increase to the object's reference count PyErr_SetString(PyExc_ValueError,"Invalid parameters"); return NULL; } //check that we have been passed array objects if (!PyArray_Check(keo_arr)){ PyErr_SetString(PyExc_TypeError,"Invalid type for keo_arr argument. Expecting Numpy array."); return NULL; } if (!PyArray_Check(data_list)){ PyErr_SetString(PyExc_TypeError,"Invalid type for data_list argument. Expecting Numpy array."); return NULL; } //check that keo_arr is two dimensional keo_arr_num_dim = PyArray_NDIM(keo_arr); if (keo_arr_num_dim != 2){ PyErr_SetString(PyExc_ValueError,"Keogram array must be two dimensional"); return NULL; } //check that data_list is 1D data_list_num_dim = PyArray_NDIM(data_list); if (data_list_num_dim != 1){ PyErr_SetString(PyExc_ValueError,"Data list array must be one dimensional"); return NULL; } //check that both are well behaved if (! PyArray_ISBEHAVED(keo_arr)){ PyErr_SetString(PyExc_ValueError,"Keogram array must be well behaved"); return NULL; } if (! PyArray_ISBEHAVED(data_list)){ PyErr_SetString(PyExc_ValueError,"Data list array must be well behaved"); return NULL; } //get the dimensions of the keogram and data_list keo_arr_dims[0] = PyArray_DIM(keo_arr, 0); keo_arr_dims[1] = PyArray_DIM(keo_arr, 1); data_list_dims[0] = PyArray_DIM(data_list, 0); //do the interpolation for(k=0;k<data_list_dims[0]-1;k++){ start_pix = *((int*)PyArray_GETPTR1(data_list,(int)k))+(strip_width/2); end_pix = *((int*)PyArray_GETPTR1(data_list,((int)k)+1))-(strip_width/2); //check that any interpolation is actually needed if (start_pix == end_pix){ continue; } //check for missing data entries if (end_pix-start_pix > max_gap){ continue; //don't interpolate over large gaps in the data. } for (y=0;y<keo_arr_dims[1];y++){ offset = keo_arr_dims[1]*y; //gradient = (keo_data[(end_pix*y_stride)+(y*x_stride)] - keo_data[(start_pix*y_stride)+(y*x_stride)])/(double)(end_pix-start_pix); gradient = (*((int*)PyArray_GETPTR2(keo_arr,end_pix,y))-*((int*)PyArray_GETPTR2(keo_arr,start_pix,y)))/(double)(end_pix-start_pix); for(x=start_pix+1;x<end_pix;x++){ //keo_data[(x*y_stride)+(y*x_stride)] = keo_data[(start_pix*y_stride)+(y*x_stride)] + (x-start_pix)*gradient; *((int*)PyArray_GETPTR2(keo_arr,x,y)) = *((int*)PyArray_GETPTR2(keo_arr,start_pix,y))+ (x-start_pix)*gradient; } } } Py_RETURN_NONE; }
static PyObject * cKeo_ct_lin_interp(PyObject *self, PyObject *args){ PyObject *keo_arr, *data_list,*colour_table; int strip_width,max_gap; npy_intp keo_arr_dims[2], data_list_dims[1],ct_dims[1]; int keo_arr_num_dim, data_list_num_dim, ct_num_dim; long int x,y,k; int start_pix, end_pix, index_in_colour_table; int start_colour, end_colour; double gradient; //parse the arguments passed to the function by Python if(!PyArg_ParseTuple(args, "OOOii", &keo_arr, &data_list,&colour_table, &strip_width, &max_gap)){ //no increase to the object's reference count PyErr_SetString(PyExc_ValueError,"Invalid parameters"); return NULL; } //check that we have been passed array objects if (!PyArray_Check(keo_arr)){ PyErr_SetString(PyExc_TypeError,"Invalid type for keo_arr argument. Expecting Numpy array."); return NULL; } if (!PyArray_Check(data_list)){ PyErr_SetString(PyExc_TypeError,"Invalid type for data_list argument. Expecting Numpy array."); return NULL; } if (!PyArray_Check(colour_table)){ PyErr_SetString(PyExc_TypeError,"Invalid type for colour_table argument. Expecting Numpy array."); return NULL; } //check that keo_arr is two dimensional keo_arr_num_dim = PyArray_NDIM(keo_arr); if (keo_arr_num_dim != 2){ PyErr_SetString(PyExc_ValueError,"Keogram array must be two dimensional"); return NULL; } //check that data_list is 1D data_list_num_dim = PyArray_NDIM(data_list); if (data_list_num_dim != 1){ PyErr_SetString(PyExc_ValueError,"Data list array must be one dimensional"); return NULL; } //check the colour_table is 1D ct_num_dim = PyArray_NDIM(colour_table); if (ct_num_dim != 1){ PyErr_SetString(PyExc_ValueError,"Colour table array must be one dimensional"); return NULL; } //check that all are well behaved if (! PyArray_ISBEHAVED(keo_arr)){ PyErr_SetString(PyExc_ValueError,"Keogram array must be well behaved"); return NULL; } if (! PyArray_ISBEHAVED(data_list)){ PyErr_SetString(PyExc_ValueError,"Data list array must be well behaved"); return NULL; } if (! PyArray_ISBEHAVED(colour_table)){ PyErr_SetString(PyExc_ValueError,"Colour table array must be well behaved"); return NULL; } //get the dimensions of the keogram, data_list and colour table keo_arr_dims[0] = PyArray_DIM(keo_arr, 0); keo_arr_dims[1] = PyArray_DIM(keo_arr, 1); data_list_dims[0] = PyArray_DIM(data_list, 0); ct_dims[0] = PyArray_DIM(colour_table, 0); /* //check stride of data list array is what we are expecting if (((int)PyArray_STRIDE(data_list, 0) != (int)sizeof(int))){ PyErr_SetString(PyExc_ValueError,"Incorrect stride for data list array"); return NULL; } //get pointers to array data keo_data = (int*)PyArray_DATA(keo_arr); data_points = (int*)PyArray_DATA(data_list); ct_data = (int*)PyArray_DATA(colour_table); */ //do the interpolation for(k=0;k<data_list_dims[0]-1;k++){ start_pix = *((int*)PyArray_GETPTR1(data_list,(int)k))+(strip_width/2); end_pix = *((int*)PyArray_GETPTR1(data_list,((int)k)+1))-(strip_width/2); // start_pix = data_points[(int)k]+(strip_width/2); // end_pix = data_points[((int)k)+1]-(strip_width/2); //check that any interpolation is actually needed if (start_pix == end_pix){ continue; } //check for missing data entries if (end_pix-start_pix > max_gap){ continue; //don't interpolate over large gaps in the data. } for (y=0;y<keo_arr_dims[1];y++){ start_colour = findIndex(colour_table,*((int*)PyArray_GETPTR2(keo_arr,start_pix,y)),ct_dims[0]); end_colour = findIndex(colour_table,*((int*)PyArray_GETPTR2(keo_arr,end_pix,y)),ct_dims[0]); gradient = (end_colour - start_colour)/(end_pix - start_pix); for(x=start_pix+1;x<end_pix;x++){ index_in_colour_table = (int)(start_colour + ((x-start_pix)*gradient)+0.5); *((int*)PyArray_GETPTR2(keo_arr,x,y)) = *((int*)PyArray_GETPTR1(colour_table,index_in_colour_table)); } } } Py_RETURN_NONE; }