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 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; }