FloatPixMapRef FPMCopySub(FloatPixMapRef pm, FPMRect rect) { FloatPixMapRef sub = FPMCreateSub(pm, rect); FloatPixMapRef result = FPMCopy(sub); FPMRelease(&sub); return result; }
void ReadCubeDestructor(void *context) { assert(context != NULL); ReadCubeContext *cx = context; FPMRelease(&cx->pm); free(cx); }
static void Destroy(FloatPixMapRef pm) { FPM_INTERNAL_ASSERT(pm != NULL); // Only "masterless" FPMs own their pixels. if (pm->master == NULL) { free(pm->pixels); } else { FPMRelease(&pm->master); } free(pm); }
FloatPixMapRef ConvertPNGData(void *data, size_t width, size_t height, size_t rowBytes, uint8_t depth, uint8_t colorType) { FloatPixMapRef result = FPMCreateC(width, height); if (result == NULL) return NULL; // Libpng transformations should have given us one of these formats. if (depth == 8 && (colorType == PNG_COLOR_TYPE_RGB_ALPHA || colorType == PNG_COLOR_TYPE_RGB)) { return ConvertPNGDataRGBA8(result, data, width, height, rowBytes); } if (depth == 16 && colorType == PNG_COLOR_TYPE_RGB_ALPHA) { return ConvertPNGDataRGBA16(result, data, width, height, rowBytes); } if (depth == 16 && colorType == PNG_COLOR_TYPE_RGB) { return ConvertPNGDataRGB16(result, data, width, height, rowBytes); } fprintf(stderr, "Unexpected PNG depth/colorType combination: %u, 0x%X\n", depth, colorType); FPMRelease(&result); return NULL; }
FloatPixMapRef FPMCreateWithPNGCustom(png_voidp ioPtr, png_rw_ptr readDataFn, FPMGammaFactor desiredGamma, FPMPNGErrorHandler errorHandler) { png_structp png = NULL; png_infop pngInfo = NULL; png_infop pngEndInfo = NULL; FloatPixMapRef result = NULL; png_uint_32 i, width, height, rowBytes; int depth, colorType; png_bytepp rows = NULL; void *data = NULL; png = png_create_read_struct(PNG_LIBPNG_VER_STRING, errorHandler, PNGError, PNGWarning); if (png == NULL) goto FAIL; pngInfo = png_create_info_struct(png); if (pngInfo == NULL) goto FAIL; pngEndInfo = png_create_info_struct(png); if (pngEndInfo == NULL) goto FAIL; if (setjmp(png_jmpbuf(png))) { // libpng will jump here on error. goto FAIL; } png_set_read_fn(png, ioPtr, readDataFn); png_read_info(png, pngInfo); if (!png_get_IHDR(png, pngInfo, &width, &height, &depth, &colorType, NULL, NULL, NULL)) goto FAIL; #if __LITTLE_ENDIAN__ if (depth == 16) png_set_swap(png); #endif if (depth <= 8 && !(colorType & PNG_COLOR_MASK_ALPHA)) { png_set_filler(png, 0xFF, PNG_FILLER_AFTER); } png_set_gray_to_rgb(png); if (depth < 8) { png_set_packing(png); png_set_expand(png); } png_read_update_info(png, pngInfo); rowBytes = png_get_rowbytes(png, pngInfo); rows = malloc(sizeof *rows * height); data = malloc(rowBytes * height); if (rows == NULL || data == NULL) goto FAIL; // Set up row pointers. for (i = 0; i < height; i++) { rows[i] = (png_bytep)data + i * rowBytes; } png_read_image(png, rows); png_read_end(png, pngEndInfo); free(rows); result = ConvertPNGData(data, width, height, rowBytes, png_get_bit_depth(png, pngInfo), png_get_color_type(png, pngInfo)); if (result != NULL) { double invGamma = 1.0/kFPMGammaSRGB; png_get_gAMA(png, pngInfo, &invGamma); FPMApplyGamma(result, 1.0/invGamma, desiredGamma, png_get_bit_depth(png, pngInfo) == 16 ? 65536 : 256); } png_destroy_read_struct(&png, &pngInfo, &pngEndInfo); free(data); return result; FAIL: FPMRelease(&result); if (png != NULL) png_destroy_read_struct(&png, &pngInfo, &pngEndInfo); free(rows); free(data); return NULL; }
bool FPMWritePNGCustom(FloatPixMapRef srcPM, png_voidp ioPtr, png_rw_ptr writeDataFn, png_flush_ptr flushDataFn, FPMWritePNGFlags options, FPMGammaFactor sourceGamma, FPMGammaFactor fileGamma, FPMPNGErrorHandler errorHandler) { if (srcPM != NULL) { bool success = false; png_structp png = NULL; png_infop pngInfo = NULL; FloatPixMapRef pm = NULL; // Prepare data. FPMDimension width = FPMGetWidth(srcPM); FPMDimension height = FPMGetHeight(srcPM); if (width > UINT32_MAX || height > UINT32_MAX) { if (errorHandler != NULL) errorHandler("image is too large for PNG format.", true); return false; } pm = FPMCopy(srcPM); if (pm == NULL) return false; unsigned steps = (options & kFPMWritePNG16BPC) ? 0x10000 : 0x100; FPMApplyGamma(pm, sourceGamma, fileGamma, steps); FPMQuantize(pm, 0.0f, 1.0f, 0.0f, steps - 1, steps, (options & kFPMQuantizeDither & kFPMQuantizeJitter) | kFMPQuantizeClip | kFMPQuantizeAlpha); png = png_create_write_struct(PNG_LIBPNG_VER_STRING, errorHandler, PNGError, PNGWarning); if (png == NULL) goto FAIL; pngInfo = png_create_info_struct(png); if (pngInfo == NULL) goto FAIL; if (setjmp(png_jmpbuf(png))) { // libpng will jump here on error. goto FAIL; } png_set_write_fn(png, ioPtr, writeDataFn, flushDataFn); png_set_IHDR(png, pngInfo, width, height, (options & kFPMWritePNG16BPC) ? 16 : 8,PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); if (fileGamma == kFPMGammaSRGB) { png_set_sRGB_gAMA_and_cHRM(png, pngInfo, PNG_sRGB_INTENT_PERCEPTUAL); } else { png_set_gAMA(png, pngInfo, fileGamma); } /* Select function used to transform a row of data to PNG-friendly format. NOTE: these work in place,and overwrite the data in pm, which is OK since we copied it. */ RowTransformer transformer = NULL; if (options & kFPMWritePNG16BPC) { transformer = TransformRow16; } else { transformer = TransformRow8; } png_write_info(png, pngInfo); size_t i; size_t rowOffset = FPMGetRowByteCount(pm); png_bytep row = (png_bytep)FPMGetBufferPointer(pm); for (i = 0; i < height; i++) { transformer(row, width); png_write_row(png, row); row += rowOffset; } png_write_end(png, pngInfo); success = true; FAIL: if (png != NULL) png_destroy_write_struct(&png, &pngInfo); FPMRelease(&pm); return success; } else { return false; } }
FloatPixMapRef FPMCreateWithPNGCustom(png_voidp ioPtr, png_rw_ptr readDataFn, FPMGammaFactor desiredGamma, FPMPNGErrorHandler errorHandler, FPMPNGProgressHandler progressHandler, void *callbackContext) { png_structp png = NULL; png_infop pngInfo = NULL; png_infop pngEndInfo = NULL; FloatPixMapRef result = NULL; png_uint_32 i, width, height, rowBytes; int depth, colorType; void *data = NULL; ErrorInfo errInfo = { errorHandler, callbackContext }; png = png_create_read_struct(PNG_LIBPNG_VER_STRING, &errInfo, PNGError, PNGWarning); if (png == NULL) goto FAIL; pngInfo = png_create_info_struct(png); if (pngInfo == NULL) goto FAIL; pngEndInfo = png_create_info_struct(png); if (pngEndInfo == NULL) goto FAIL; if (setjmp(png_jmpbuf(png))) { // libpng will jump here on error. goto FAIL; } png_set_read_fn(png, ioPtr, readDataFn); png_read_info(png, pngInfo); if (!png_get_IHDR(png, pngInfo, &width, &height, &depth, &colorType, NULL, NULL, NULL)) goto FAIL; #if FPM_LITTLE_ENDIAN if (depth == 16) png_set_swap(png); #endif if (depth <= 8 && !(colorType & PNG_COLOR_MASK_ALPHA)) { png_set_filler(png, 0xFF, PNG_FILLER_AFTER); } png_set_gray_to_rgb(png); if (depth < 8) { png_set_packing(png); png_set_expand(png); } if (colorType & PNG_COLOR_MASK_PALETTE) { png_set_palette_to_rgb(png); } png_read_update_info(png, pngInfo); rowBytes = png_get_rowbytes(png, pngInfo); data = malloc(rowBytes * height); if (data == NULL) goto FAIL; // Set up row pointers. for (i = 0; i < height; i++) { png_read_row(png, (png_bytep)data + i * rowBytes, NULL); if (progressHandler) progressHandler((float)(i + 1) / (float)height, callbackContext); } png_read_end(png, pngEndInfo); result = ConvertPNGData(data, width, height, rowBytes, png_get_bit_depth(png, pngInfo), png_get_color_type(png, pngInfo)); if (result != NULL) { double invGamma = 1.0/kFPMGammaSRGB; png_get_gAMA(png, pngInfo, &invGamma); FPMApplyGamma(result, 1.0/invGamma, desiredGamma, png_get_bit_depth(png, pngInfo) == 16 ? 65536 : 256); } else { if (errorHandler != NULL) errorHandler("Could not convert PNG data to native representation.", true, callbackContext); } png_destroy_read_struct(&png, &pngInfo, &pngEndInfo); free(data); return result; FAIL: FPMRelease(&result); if (png != NULL) png_destroy_read_struct(&png, &pngInfo, &pngEndInfo); free(data); return NULL; }