static void copyBGRDataFromMMBitmap(MMBitmapRef bitmap, uint8_t *dest) { if (MMRGB_IS_BGR && (bitmap->bytewidth % 4) == 0) { /* No conversion needed. */ memcpy(dest, bitmap->imageBuffer, bitmap->bytewidth * bitmap->height); } else { /* Convert to RGB with other-than-4-byte alignment. */ const size_t bytewidth = (bitmap->width * bitmap->bytesPerPixel + 3) & ~3; size_t y; /* Copy image data row by row. */ for (y = 0; y < bitmap->height; ++y) { uint8_t *rowptr = dest + (y * bytewidth); size_t x; for (x = 0; x < bitmap->width; ++x) { MMRGBColor *color = MMRGBColorRefAtPoint(bitmap, x, y); /* BMP files are stored in BGR format. */ rowptr[0] = color->blue; rowptr[1] = color->green; rowptr[2] = color->red; rowptr += bitmap->bytesPerPixel; } } } }
/* Returns pointer to PNGWriteInfo struct containing data ready to be used with * functions such as png_write_png(). * * It is the caller's responsibility to destroy() the returned structure with * destroyPNGWriteInfo(). */ static PNGWriteInfoRef createPNGWriteInfo(MMBitmapRef bitmap) { PNGWriteInfoRef info = malloc(sizeof(PNGWriteInfo)); png_uint_32 y; if (info == NULL) return NULL; info->png_ptr = NULL; info->info_ptr = NULL; info->row_pointers = NULL; assert(bitmap != NULL); /* Initialize the write struct. */ info->png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (info->png_ptr == NULL) goto bail; /* Set up error handling. */ if (setjmp(png_jmpbuf(info->png_ptr))) { png_destroy_write_struct(&(info->png_ptr), &(info->info_ptr)); goto bail; } /* Initialize the info struct. */ info->info_ptr = png_create_info_struct(info->png_ptr); if (info->info_ptr == NULL) { png_destroy_write_struct(&(info->png_ptr), NULL); goto bail; } /* Set image attributes. */ png_set_IHDR(info->png_ptr, info->info_ptr, bitmap->width, bitmap->height, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); info->row_count = bitmap->height; info->row_pointers = png_malloc(info->png_ptr, sizeof(png_byte *) * info->row_count); if (bitmap->bytesPerPixel == 3) { /* No alpha channel; image data can be copied directly. */ for (y = 0; y < info->row_count; ++y) { info->row_pointers[y] = bitmap->imageBuffer + (bitmap->bytewidth * y); } info->free_row_pointers = false; /* Convert BGR to RGB if necessary. */ if (MMRGB_IS_BGR) { png_set_bgr(info->png_ptr); } } else { /* Ignore alpha channel; copy image data row by row. */ const size_t bytesPerPixel = 3; const size_t bytewidth = ADD_PADDING(bitmap->width * bytesPerPixel); for (y = 0; y < info->row_count; ++y) { png_uint_32 x; png_byte *row_ptr = png_malloc(info->png_ptr, bytewidth); info->row_pointers[y] = row_ptr; for (x = 0; x < bitmap->width; ++x) { MMRGBColor *color = MMRGBColorRefAtPoint(bitmap, x, y); row_ptr[0] = color->red; row_ptr[1] = color->green; row_ptr[2] = color->blue; row_ptr += bytesPerPixel; } } info->free_row_pointers = true; } png_set_rows(info->png_ptr, info->info_ptr, info->row_pointers); return info; bail: if (info != NULL) free(info); return NULL; }