static UncompressedImage CGImageToUncompressedImage(CGImageRef image) { if (image == nullptr) return UncompressedImage(); size_t width = CGImageGetWidth(image); size_t height = CGImageGetHeight(image); if ((0 == width) || (0 == height)) return UncompressedImage(); size_t bits_per_pixel = CGImageGetBitsPerPixel(image); size_t bits_per_component = CGImageGetBitsPerComponent(image); CGColorSpaceRef colorspace = CGImageGetColorSpace(image); size_t row_size; UncompressedImage::Format format; CGColorSpaceRef bitmap_colorspace; CGBitmapInfo bitmap_info; if ((8 == bits_per_pixel) && (8 == bits_per_component) && (CGColorSpaceGetModel(colorspace) == kCGColorSpaceModelMonochrome)) { row_size = width; format = UncompressedImage::Format::GRAY; static CGColorSpaceRef grey_colorspace = CGColorSpaceCreateDeviceGray(); bitmap_colorspace = grey_colorspace; bitmap_info = 0; } else { static CGColorSpaceRef rgb_colorspace = CGColorSpaceCreateDeviceRGB(); bitmap_colorspace = rgb_colorspace; if ((24 == bits_per_pixel) && (8 == bits_per_component)) { row_size = width * 3; format = UncompressedImage::Format::RGB; bitmap_info = kCGBitmapByteOrder32Big; } else { row_size = width * 4; format = UncompressedImage::Format::RGBA; bitmap_info = kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big; } } std::unique_ptr<uint8_t[]> uncompressed(new uint8_t[height * row_size]); CGContextRef bitmap = CGBitmapContextCreate(uncompressed.get(), width, height, 8, row_size, bitmap_colorspace, bitmap_info); if (nullptr == bitmap) { return UncompressedImage(); } AtScopeExit(bitmap) { CFRelease(bitmap); }; CGContextDrawImage(bitmap, CGRectMake(0, 0, width, height), image); return UncompressedImage(format, row_size, width, height, std::move(uncompressed)); }
UncompressedImage LoadPNG(Path path) { FileMapping map(path); if (map.error()) return UncompressedImage(); return LoadPNG(map.data(), map.size()); }
UncompressedImage LoadPNG(const void *data, size_t size) { png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); if (png_ptr == nullptr) return UncompressedImage(); png_infop info_ptr = png_create_info_struct(png_ptr); if (info_ptr == nullptr) { png_destroy_read_struct(&png_ptr, nullptr, nullptr); return UncompressedImage(); } UncompressedImage result = LoadPNG(png_ptr, info_ptr, data, size); png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); return result; }
static UncompressedImage LoadTiff(TIFFRGBAImage &img) { if (img.width > 8192 || img.height > 8192) throw std::runtime_error("TIFF file is too large"); std::unique_ptr<uint8_t[]> data(new uint8_t[img.width * img.height * 4]); uint32_t *data32 = (uint32_t *)(void *)data.get(); if (!TIFFRGBAImageGet(&img, data32, img.width, img.height)) throw std::runtime_error("Failed to copy TIFF data"); return UncompressedImage(UncompressedImage::Format::RGBA, img.width * 4, img.width, img.height, std::move(data), true); }
UncompressedImage LoadJPEGFile(Path path) { CGDataProviderRef data_provider = CGDataProviderCreateWithFilename(path.c_str()); if (nullptr == data_provider) return UncompressedImage(); CGImageRef image = CGImageCreateWithJPEGDataProvider( data_provider, nullptr, false, kCGRenderingIntentDefault); UncompressedImage result = CGImageToUncompressedImage(image); if (nullptr != image) CFRelease(image); CFRelease(data_provider); return result; }
UncompressedImage LoadPNG(const void *data, size_t size) { CGDataProviderRef data_provider = CGDataProviderCreateWithData( nullptr, data, size, nullptr); if (nullptr == data_provider) return UncompressedImage(); CGImageRef image = CGImageCreateWithPNGDataProvider( data_provider, nullptr, false, kCGRenderingIntentDefault); UncompressedImage result = CGImageToUncompressedImage(image); if (nullptr != image) CFRelease(image); CFRelease(data_provider); return result; }
static UncompressedImage LoadPNG(png_structp png_ptr, png_infop info_ptr, const void *data, size_t size) { assert(data != nullptr); PNGCallbackContext ctx{(const uint8_t *)data}; png_set_read_fn(png_ptr, &ctx, PNGReadCallback); png_read_info(png_ptr, info_ptr); png_uint_32 width, height; int bit_depth, color_type, interlace_type; png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, nullptr, nullptr); /* shrink 16 bit to 8 bit */ png_set_strip_16(png_ptr); /* grow 1,2,4 bit to 8 bit */ png_set_expand(png_ptr); if (color_type == PNG_COLOR_TYPE_PALETTE) /* no thanks, we don't want a palette, give us RGB or gray instead */ png_set_palette_to_rgb(png_ptr); png_read_update_info(png_ptr, info_ptr); png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, nullptr, nullptr); /* check the color type */ const UncompressedImage::Format format = ConvertColorType(color_type); if (format == UncompressedImage::Format::INVALID) return UncompressedImage::Invalid(); /* allocate memory for the uncompressed pixels */ const unsigned num_channels = png_get_channels(png_ptr, info_ptr); const unsigned pitch = (num_channels * bit_depth) / 8 * width; uint8_t *uncompressed = new uint8_t[pitch * height]; if (uncompressed == nullptr) return UncompressedImage::Invalid(); png_bytep *rows = new png_bytep[height]; if (rows == nullptr) { delete[] uncompressed; return UncompressedImage::Invalid(); } for (unsigned i = 0; i < height; ++i) rows[i] = uncompressed + i * pitch; /* uncompress and import into an OpenGL texture */ png_read_image(png_ptr, rows); delete[] rows; return UncompressedImage(format, pitch, width, height, uncompressed); }
UncompressedImage LoadPNGFile(const TCHAR *path) { FILE *file = _tfopen(path, _T("rb")); if (file == nullptr) return UncompressedImage::Invalid(); /* Create and initialize the png_struct * with the desired error handler * functions. If you want to use the * default stderr and longjump method, * you can supply NULL for the last * three parameters. We also supply the * the compiler header file version, so * that we know if the application * was compiled with a compatible version * of the library. REQUIRED */ png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); if (png_ptr == nullptr) { fclose(file); return UncompressedImage::Invalid(); } /* Allocate/initialize the memory * for image information. REQUIRED. */ png_infop info_ptr = png_create_info_struct(png_ptr); if (info_ptr == nullptr) { fclose(file); png_destroy_read_struct(&png_ptr, nullptr, nullptr); return UncompressedImage::Invalid(); } /* Set error handling if you are * using the setjmp/longjmp method * (this is the normal method of * doing things with libpng). * REQUIRED unless you set up * your own error handlers in * the png_create_read_struct() * earlier. */ if (setjmp(png_jmpbuf(png_ptr))) { /* Free all of the memory associated * with the png_ptr and info_ptr */ png_destroy_read_struct(&png_ptr, &info_ptr, NULL); fclose(file); /* If we get here, we had a * problem reading the file */ return UncompressedImage::Invalid(); } /* Set up the output control if * you are using standard C streams */ png_init_io(png_ptr, file); /* If we have already * read some of the signature */ unsigned int sig_read = 0; png_set_sig_bytes(png_ptr, sig_read); /* * If you have enough memory to read * in the entire image at once, and * you need to specify only * transforms that can be controlled * with one of the PNG_TRANSFORM_* * bits (this presently excludes * dithering, filling, setting * background, and doing gamma * adjustment), then you can read the * entire image (including pixels) * into the info structure with this * call * * PNG_TRANSFORM_STRIP_16 | * PNG_TRANSFORM_PACKING forces 8 bit * PNG_TRANSFORM_EXPAND forces to * expand a palette into RGB */ png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_STRIP_16 | PNG_TRANSFORM_PACKING | PNG_TRANSFORM_EXPAND | PNG_TRANSFORM_GRAY_TO_RGB, NULL); png_uint_32 width, height; int bit_depth, color_type, interlace_type; png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL); /* check the color type */ const UncompressedImage::Format format = ConvertColorType(color_type); if (format == UncompressedImage::Format::INVALID) { fclose(file); return UncompressedImage::Invalid(); } /* allocate memory for the uncompressed pixels */ unsigned int row_bytes = png_get_rowbytes(png_ptr, info_ptr); uint8_t *uncompressed = new uint8_t[row_bytes * height]; png_bytepp row_pointers = png_get_rows(png_ptr, info_ptr); for (png_uint_32 i = 0; i < height; ++i) { memcpy(uncompressed + (row_bytes * i), row_pointers[i], row_bytes); } /* Clean up after the read, * and free any memory allocated */ png_destroy_read_struct(&png_ptr, &info_ptr, NULL); /* Close the file */ fclose(file); /* That's it */ return UncompressedImage(format, row_bytes, width, height, uncompressed); }
static UncompressedImage Invalid() { return UncompressedImage(Format::INVALID, 0, 0, 0, nullptr); }
UncompressedImage LoadJPEGFile(const char *path) { FILE *file = fopen(path, "rb"); if (file == nullptr) return UncompressedImage::Invalid(); struct jpeg_decompress_struct cinfo; JPEGErrorManager err; cinfo.err = jpeg_std_error(&err.base); if (setjmp(err.setjmp_buffer)) { jpeg_destroy_decompress(&cinfo); fclose(file); return UncompressedImage::Invalid(); } jpeg_create_decompress(&cinfo); jpeg_stdio_src(&cinfo, file); jpeg_read_header(&cinfo, (boolean)true); if (cinfo.num_components != 3) { jpeg_destroy_decompress(&cinfo); fclose(file); return UncompressedImage::Invalid(); } cinfo.out_color_space = JCS_RGB; cinfo.quantize_colors = (boolean)false; jpeg_calc_output_dimensions(&cinfo); jpeg_start_decompress(&cinfo); const unsigned width = cinfo.output_width; const unsigned height = cinfo.output_height; const size_t row_size = 3 * width; const size_t image_buffer_size = row_size * height; const size_t row_buffer_size = row_size; /* allocate a buffer that holds the uncompressed image plus a row buffer with packed 24 bit samples (for libjpeg) */ uint8_t *const image_buffer = new uint8_t[image_buffer_size + row_buffer_size]; if (image_buffer == nullptr) { jpeg_destroy_decompress(&cinfo); fclose(file); return UncompressedImage::Invalid(); } uint8_t *const row = image_buffer + image_buffer_size; JSAMPROW rowptr[1] = { row }; uint8_t *p = image_buffer; while (cinfo.output_scanline < height) { jpeg_read_scanlines(&cinfo, rowptr, (JDIMENSION)1); p = std::copy(row, row + row_size, p); } assert(p == image_buffer + image_buffer_size); jpeg_finish_decompress(&cinfo); jpeg_destroy_decompress(&cinfo); fclose(file); return UncompressedImage(UncompressedImage::Format::RGB, row_size, width, height, image_buffer); }