MMBitmapRef copyMMBitmapFromPortion(MMBitmapRef source, MMRect rect) { assert(source != NULL); if (source->imageBuffer == NULL || !MMBitmapRectInBounds(source, rect)) { return NULL; } else { uint8_t *copiedBuf = NULL; const size_t bufsize = rect.size.height * source->bytewidth; const size_t offset = (source->bytewidth * rect.origin.y) + (rect.origin.x * source->bytesPerPixel); /* Don't go over the bounds, programmer! */ assert((bufsize + offset) <= (source->bytewidth * source->height)); copiedBuf = malloc(bufsize); if (copiedBuf == NULL) return NULL; memcpy(copiedBuf, source->imageBuffer + offset, bufsize); return createMMBitmap(copiedBuf, rect.size.width, rect.size.height, source->bytewidth, source->bitsPerPixel, source->bytesPerPixel); } }
MMBitmapRef copyMMBitmap(MMBitmapRef bitmap) { uint8_t *copiedBuf = NULL; assert(bitmap != NULL); if (bitmap->imageBuffer != NULL) { const size_t bufsize = bitmap->height * bitmap->bytewidth; copiedBuf = malloc(bufsize); if (copiedBuf == NULL) return NULL; memcpy(copiedBuf, bitmap->imageBuffer, bufsize); } return createMMBitmap(copiedBuf, bitmap->width, bitmap->height, bitmap->bytewidth, bitmap->bitsPerPixel, bitmap->bytesPerPixel); }
MMBitmapRef copyMMBitmapFromDisplayInRect(MMRect rect) { #if defined(IS_MACOSX) /* The following is a very modified version of the glGrab code example * given by Apple (as are some of the convenience functions called). */ size_t bytewidth; uint8_t bitsPerPixel, bytesPerPixel; uint8_t *buffer; /* Build OpenGL context of entire screen */ CGDirectDisplayID displayID = CGMainDisplayID(); CGOpenGLDisplayMask mask = CGDisplayIDToOpenGLDisplayMask(displayID); CGLContextObj glContext = createFullScreenCGLContext(mask); if (glContext == NULL) return NULL; /* TODO: CGDisplayBitsPerPixel() is deprecated in Snow Leopard; I'm not * sure of the replacement function. */ bitsPerPixel = (uint8_t)CGDisplayBitsPerPixel(displayID); bytesPerPixel = bitsPerPixel / 8; /* Align width to padding. */ bytewidth = ADD_PADDING(rect.size.width * bytesPerPixel); /* Convert Quartz point to postscript point. */ rect.origin.y = CGDisplayPixelsHigh(displayID) - rect.origin.y - rect.size.height; /* Extract buffer from context */ buffer = createBufferFromCurrentCGLContext((GLint)rect.origin.x, (GLint)rect.origin.y, (GLsizei)rect.size.width, (GLsizei)rect.size.height, bytewidth); /* Reset and release GL context */ destroyFullScreenCGLContext(glContext); if (buffer == NULL) return NULL; /* Convert from OpenGL (origin at bottom left) to Quartz (origin at top * left) coordinate system. */ flipBitmapData(buffer, rect.size.width, rect.size.height, bytewidth); return createMMBitmap(buffer, rect.size.width, rect.size.height, bytewidth, bitsPerPixel, bytesPerPixel); #elif defined(USE_X11) MMBitmapRef bitmap; Display *display = XOpenDisplay(NULL); XImage *image = XGetImage(display, XDefaultRootWindow(display), (int)rect.origin.x, (int)rect.origin.y, (unsigned int)rect.size.width, (unsigned int)rect.size.height, AllPlanes, ZPixmap); XCloseDisplay(display); if (image == NULL) return NULL; bitmap = createMMBitmap((uint8_t *)image->data, rect.size.width, rect.size.height, (size_t)image->bytes_per_line, (uint8_t)image->bits_per_pixel, (uint8_t)image->bits_per_pixel / 8); image->data = NULL; /* Steal ownership of bitmap data so we don't have to * copy it. */ XDestroyImage(image); return bitmap; #elif defined(IS_WINDOWS) MMBitmapRef bitmap; void *data; HDC screen = NULL, screenMem = NULL; HBITMAP dib; BITMAPINFO bi; /* Initialize bitmap info. */ bi.bmiHeader.biSize = sizeof(bi.bmiHeader); bi.bmiHeader.biWidth = (long)rect.size.width; bi.bmiHeader.biHeight = -(long)rect.size.height; /* Non-cartesian, please */ bi.bmiHeader.biPlanes = 1; bi.bmiHeader.biBitCount = 32; bi.bmiHeader.biCompression = BI_RGB; bi.bmiHeader.biSizeImage = (DWORD)(4 * rect.size.width * rect.size.height); bi.bmiHeader.biXPelsPerMeter = 0; bi.bmiHeader.biYPelsPerMeter = 0; bi.bmiHeader.biClrUsed = 0; bi.bmiHeader.biClrImportant = 0; screen = GetDC(NULL); /* Get entire screen */ if (screen == NULL) return NULL; /* Get screen data in display device context. */ dib = CreateDIBSection(screen, &bi, DIB_RGB_COLORS, &data, NULL, 0); /* Copy the data into a bitmap struct. */ if ((screenMem = CreateCompatibleDC(screen)) == NULL || SelectObject(screenMem, dib) == NULL || !BitBlt(screenMem, (int)rect.origin.x, (int)rect.origin.y, (int)rect.size.width, (int)rect.size.height, screen, 0, 0, SRCCOPY)) { /* Error copying data. */ ReleaseDC(NULL, screen); DeleteObject(dib); if (screenMem != NULL) DeleteDC(screenMem); return NULL; } bitmap = createMMBitmap(NULL, rect.size.width, rect.size.height, 4 * rect.size.width, (uint8_t)bi.bmiHeader.biBitCount, 4); /* Copy the data to our pixel buffer. */ if (bitmap != NULL) { bitmap->imageBuffer = malloc(bitmap->bytewidth * bitmap->height); memcpy(bitmap->imageBuffer, data, bitmap->bytewidth * bitmap->height); } ReleaseDC(NULL, screen); DeleteObject(dib); DeleteDC(screenMem); return bitmap; #endif }
MMBitmapRef newMMBitmapFromPNG(const char *path, MMPNGReadError *err) { FILE *fp; uint8_t header[8]; png_struct *png_ptr = NULL; png_info *info_ptr = NULL; png_byte bit_depth, color_type; uint8_t *row, *bitmapData; uint8_t bytesPerPixel; png_uint_32 width, height, y; uint32_t bytewidth; if ((fp = fopen(path, "rb")) == NULL) { if (err != NULL) *err = kPNGAccessError; return NULL; } /* Initialize error code to generic value. */ if (err != NULL) *err = kPNGGenericError; /* Validate the PNG. */ if (fread(header, 1, sizeof header, fp) == 0) { if (err != NULL) *err = kPNGReadError; goto bail; } else if (!png_check_sig(header, sizeof(header))) { if (err != NULL) *err = kPNGInvalidHeaderError; goto bail; } png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (png_ptr == NULL) goto bail; info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) goto bail; /* Set up error handling. */ if (setjmp(png_jmpbuf(png_ptr))) { goto bail; } png_init_io(png_ptr, fp); /* Skip past the header. */ png_set_sig_bytes(png_ptr, sizeof header); png_read_info(png_ptr, info_ptr); /* Convert different image types to common type to be read. */ bit_depth = png_get_bit_depth(png_ptr, info_ptr); color_type = png_get_color_type(png_ptr, info_ptr); /* Convert color palettes to RGB. */ if (color_type == PNG_COLOR_TYPE_PALETTE) { png_set_palette_to_rgb(png_ptr); } /* Convert PNG to bit depth of 8. */ if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) { #ifdef PNG_LIBPNG_VER_SONUM #if PNG_LIBPNG_VER_SONUM >= 14 png_set_expand_gray_1_2_4_to_8(png_ptr); #else png_set_gray_1_2_4_to_8(png_ptr); #endif #else png_set_gray_1_2_4_to_8(png_ptr); #endif } else if (bit_depth == 16) { png_set_strip_16(png_ptr); } /* Convert transparency chunk to alpha channel. */ if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { png_set_tRNS_to_alpha(png_ptr); } /* Convert gray images to RGB. */ if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { png_set_gray_to_rgb(png_ptr); } /* Ignore alpha for now. */ if (color_type & PNG_COLOR_MASK_ALPHA) { png_set_strip_alpha(png_ptr); } /* Get image attributes. */ width = png_get_image_width(png_ptr, info_ptr); height = png_get_image_height(png_ptr, info_ptr); bytesPerPixel = 3; /* All images decompress to this size. */ bytewidth = ADD_PADDING(width * bytesPerPixel); /* Align width. */ /* Decompress the PNG row by row. */ bitmapData = calloc(1, bytewidth * height); row = png_malloc(png_ptr, png_get_rowbytes(png_ptr, info_ptr)); if (bitmapData == NULL || row == NULL) goto bail; for (y = 0; y < height; ++y) { png_uint_32 x; const uint32_t rowOffset = y * bytewidth; uint8_t *rowptr = row; png_read_row(png_ptr, (png_byte *)row, NULL); for (x = 0; x < width; ++x) { const uint32_t colOffset = x * bytesPerPixel; MMRGBColor *color = (MMRGBColor *)(bitmapData + rowOffset + colOffset); color->red = *rowptr++; color->green = *rowptr++; color->blue = *rowptr++; } } free(row); /* Finish reading. */ png_read_end(png_ptr, NULL); png_destroy_read_struct(&png_ptr, &info_ptr, NULL); fclose(fp); return createMMBitmap(bitmapData, width, height, bytewidth, bytesPerPixel * 8, bytesPerPixel); bail: png_destroy_read_struct(&png_ptr, &info_ptr, NULL); fclose(fp); return NULL; }
MMBitmapRef newMMBitmapFromBMP(const char *path, MMBMPReadError *err) { FILE *fp; struct BITMAP_FILE_HEADER fileHeader = {0}; /* Initialize elements to 0. */ struct BITMAP_INFO_HEADER dibHeader = {0}; uint32_t headerSize = 0; uint8_t bytesPerPixel; size_t bytewidth; uint8_t *imageBuf; if ((fp = fopen(path, "rb")) == NULL) { if (err != NULL) *err = kBMPAccessError; return NULL; } /* Initialize error code to generic value. */ if (err != NULL) *err = kBMPGenericError; if (fread(&fileHeader, sizeof(fileHeader), 1, fp) == 0) goto bail; /* Convert from little-endian if it's not already. */ convertBitmapFileHeader(&fileHeader); /* First two bytes should always be 0x4D42. */ if (fileHeader.magic != BMP_MAGIC) { if (err != NULL) *err = kBMPInvalidKeyError; goto bail; } /* Get header size. */ if (fread(&headerSize, sizeof(headerSize), 1, fp) == 0) goto bail; headerSize = swapLittleAndHost32(headerSize); /* Back up before reading header. */ if (fseek(fp, -(long)sizeof(headerSize), SEEK_CUR) < 0) goto bail; if (headerSize == 12) { /* OS/2 v1 header */ struct BITMAP_CORE_HEADER coreHeader = {0}; if (fread(&coreHeader, sizeof(coreHeader), 1, fp) == 0) goto bail; dibHeader.width = coreHeader.width; dibHeader.height = coreHeader.height; dibHeader.colorPlanes = coreHeader.colorPlanes; dibHeader.bitsPerPixel = coreHeader.bitsPerPixel; } else if (headerSize == 40 || headerSize == 108 || headerSize == 124) { /* Windows v3/v4/v5 header */ /* Read only the common part (v3) and skip over the rest. */ if (fread(&dibHeader, sizeof(dibHeader), 1, fp) == 0) goto bail; } else { if (err != NULL) *err = kBMPUnsupportedHeaderError; goto bail; } convertBitmapInfoHeader(&dibHeader); if (dibHeader.colorPlanes != 1) { if (err != NULL) *err = kBMPInvalidColorPanesError; goto bail; } /* Currently only 24-bit and 32-bit are supported. */ if (dibHeader.bitsPerPixel != 24 && dibHeader.bitsPerPixel != 32) { if (err != NULL) *err = kBMPUnsupportedColorDepthError; goto bail; } if (dibHeader.compression != kBMP_RGB) { if (err != NULL) *err = kBMPUnsupportedCompressionError; goto bail; } /* This can happen because we don't fully parse Windows v4/v5 headers. */ if (ftell(fp) != (long)fileHeader.imageOffset) { fseek(fp, fileHeader.imageOffset, SEEK_SET); } /* Get bytes per row, including padding. */ bytesPerPixel = dibHeader.bitsPerPixel / 8; bytewidth = ADD_PADDING(dibHeader.width * bytesPerPixel); imageBuf = readImageData(fp, dibHeader.width, abs(dibHeader.height), bytesPerPixel, bytewidth); fclose(fp); if (imageBuf == NULL) { if (err != NULL) *err = kBMPInvalidPixelDataError; return NULL; } /* A negative height indicates that the image is flipped. * * We store our bitmaps as "flipped" according to the BMP format; i.e., (0, 0) * is the top left, not bottom left. So we only need to flip the bitmap if * the height is NOT negative. */ if (dibHeader.height < 0) { dibHeader.height = -dibHeader.height; } else { flipBitmapData(imageBuf, dibHeader.width, dibHeader.height, bytewidth); } return createMMBitmap(imageBuf, dibHeader.width, dibHeader.height, bytewidth, (uint8_t)dibHeader.bitsPerPixel, bytesPerPixel); bail: fclose(fp); return NULL; }
MMBitmapRef copyMMBitmapFromDisplayInRect(MMRect rect) { #if defined(IS_MACOSX) size_t bytewidth; uint8_t bitsPerPixel, bytesPerPixel; uint8_t *buffer; CGDirectDisplayID displayID = CGMainDisplayID(); //Replacement for CGDisplayBitsPerPixel. CGDisplayModeRef mode = CGDisplayCopyDisplayMode(displayID); size_t depth = 0; CFStringRef pixEnc = CGDisplayModeCopyPixelEncoding(mode); if(CFStringCompare(pixEnc, CFSTR(IO32BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo) depth = 32; else if(CFStringCompare(pixEnc, CFSTR(IO16BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo) depth = 16; else if(CFStringCompare(pixEnc, CFSTR(IO8BitIndexedPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo) depth = 8; bitsPerPixel = (uint8_t) depth; bytesPerPixel = bitsPerPixel / 8; /* Align width to padding. */ bytewidth = ADD_PADDING(rect.size.width * bytesPerPixel); /* Convert Quartz point to postscript point. */ rect.origin.y = CGDisplayPixelsHigh(displayID) - rect.origin.y - rect.size.height; CGImageRef image = CGDisplayCreateImageForRect(displayID, CGRectMake(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height)); // Request access to the raw pixel data via the image's DataProvider. CGDataProviderRef provider = CGImageGetDataProvider(image); CFDataRef data = CGDataProviderCopyData(provider); size_t width, height; width = CGImageGetWidth(image); height = CGImageGetHeight(image); size_t bpp = CGImageGetBitsPerPixel(image) / 8; uint8 *pixels = malloc(width * height * bpp); memcpy(pixels, CFDataGetBytePtr(data), width * height * bpp); CFRelease(data); CGImageRelease(image); return createMMBitmap(pixels, rect.size.width, rect.size.height, bytewidth, bitsPerPixel, bytesPerPixel); #elif defined(USE_X11) MMBitmapRef bitmap; Display *display = XOpenDisplay(NULL); XImage *image = XGetImage(display, XDefaultRootWindow(display), (int)rect.origin.x, (int)rect.origin.y, (unsigned int)rect.size.width, (unsigned int)rect.size.height, AllPlanes, ZPixmap); XCloseDisplay(display); if (image == NULL) return NULL; bitmap = createMMBitmap((uint8_t *)image->data, rect.size.width, rect.size.height, (size_t)image->bytes_per_line, (uint8_t)image->bits_per_pixel, (uint8_t)image->bits_per_pixel / 8); image->data = NULL; /* Steal ownership of bitmap data so we don't have to * copy it. */ XDestroyImage(image); return bitmap; #elif defined(IS_WINDOWS) MMBitmapRef bitmap; void *data; HDC screen = NULL, screenMem = NULL; HBITMAP dib; BITMAPINFO bi; /* Initialize bitmap info. */ bi.bmiHeader.biSize = sizeof(bi.bmiHeader); bi.bmiHeader.biWidth = (long)rect.size.width; bi.bmiHeader.biHeight = -(long)rect.size.height; /* Non-cartesian, please */ bi.bmiHeader.biPlanes = 1; bi.bmiHeader.biBitCount = 32; bi.bmiHeader.biCompression = BI_RGB; bi.bmiHeader.biSizeImage = (DWORD)(4 * rect.size.width * rect.size.height); bi.bmiHeader.biXPelsPerMeter = 0; bi.bmiHeader.biYPelsPerMeter = 0; bi.bmiHeader.biClrUsed = 0; bi.bmiHeader.biClrImportant = 0; screen = GetDC(NULL); /* Get entire screen */ if (screen == NULL) return NULL; /* Get screen data in display device context. */ dib = CreateDIBSection(screen, &bi, DIB_RGB_COLORS, &data, NULL, 0); /* Copy the data into a bitmap struct. */ if ((screenMem = CreateCompatibleDC(screen)) == NULL || SelectObject(screenMem, dib) == NULL || !BitBlt(screenMem, (int)rect.origin.x, (int)rect.origin.y, (int)rect.size.width, (int)rect.size.height, screen, 0, 0, SRCCOPY)) { /* Error copying data. */ ReleaseDC(NULL, screen); DeleteObject(dib); if (screenMem != NULL) DeleteDC(screenMem); return NULL; } bitmap = createMMBitmap(NULL, rect.size.width, rect.size.height, 4 * rect.size.width, (uint8_t)bi.bmiHeader.biBitCount, 4); /* Copy the data to our pixel buffer. */ if (bitmap != NULL) { bitmap->imageBuffer = malloc(bitmap->bytewidth * bitmap->height); memcpy(bitmap->imageBuffer, data, bitmap->bytewidth * bitmap->height); } ReleaseDC(NULL, screen); DeleteObject(dib); DeleteDC(screenMem); return bitmap; #endif }