bool ReadLatLongConstructor(FloatPixMapRef sourceImage, RenderFlags flags, SphericalPixelSourceFunction *source, void **context) { if (sourceImage == NULL || context == NULL) return false; ReadLatLongContext *cx = malloc(sizeof (ReadLatLongContext)); if (cx == NULL) return false; cx->pm = FPMRetain(sourceImage); cx->pwidth = FPMGetWidth(sourceImage); cx->width = (float)cx->pwidth / (2.0f * kPiF); cx->height = (float)FPMGetHeight(sourceImage) / kPiF; if (flags & kRenderFast) *source = ReadLatLongFast; else *source = ReadLatLong; *context = cx; return true; }
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; } }