| /* |
| * Copyright 2000 Corel Corporation |
| * Copyright 2006 Marcus Meissner |
| * Copyright 2006 CodeWeavers, Aric Stewart |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| */ |
| |
| #include "config.h" |
| #include "wine/port.h" |
| |
| #include <stdarg.h> |
| #include <stdio.h> |
| |
| #include "gphoto2_i.h" |
| #include "wingdi.h" |
| #include "winuser.h" |
| #include "wine/library.h" |
| #include "wine/debug.h" |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(twain); |
| |
| #ifdef HAVE_GPHOTO2 |
| static void *libjpeg_handle; |
| #define MAKE_FUNCPTR(f) static typeof(f) * p##f |
| MAKE_FUNCPTR(jpeg_std_error); |
| MAKE_FUNCPTR(jpeg_CreateDecompress); |
| MAKE_FUNCPTR(jpeg_read_header); |
| MAKE_FUNCPTR(jpeg_start_decompress); |
| MAKE_FUNCPTR(jpeg_read_scanlines); |
| MAKE_FUNCPTR(jpeg_finish_decompress); |
| MAKE_FUNCPTR(jpeg_destroy_decompress); |
| #undef MAKE_FUNCPTR |
| |
| static void *load_libjpeg(void) |
| { |
| if((libjpeg_handle = wine_dlopen(SONAME_LIBJPEG, RTLD_NOW, NULL, 0)) != NULL) { |
| |
| #define LOAD_FUNCPTR(f) \ |
| if((p##f = wine_dlsym(libjpeg_handle, #f, NULL, 0)) == NULL) { \ |
| libjpeg_handle = NULL; \ |
| return NULL; \ |
| } |
| |
| LOAD_FUNCPTR(jpeg_std_error); |
| LOAD_FUNCPTR(jpeg_CreateDecompress); |
| LOAD_FUNCPTR(jpeg_read_header); |
| LOAD_FUNCPTR(jpeg_start_decompress); |
| LOAD_FUNCPTR(jpeg_read_scanlines); |
| LOAD_FUNCPTR(jpeg_finish_decompress); |
| LOAD_FUNCPTR(jpeg_destroy_decompress); |
| #undef LOAD_FUNCPTR |
| } |
| return libjpeg_handle; |
| } |
| |
| |
| /* for the jpeg decompressor source manager. */ |
| static void _jpeg_init_source(j_decompress_ptr cinfo) { } |
| |
| static boolean _jpeg_fill_input_buffer(j_decompress_ptr cinfo) { |
| ERR("(), should not get here.\n"); |
| return FALSE; |
| } |
| |
| static void _jpeg_skip_input_data(j_decompress_ptr cinfo,long num_bytes) { |
| TRACE("Skipping %ld bytes...\n", num_bytes); |
| cinfo->src->next_input_byte += num_bytes; |
| cinfo->src->bytes_in_buffer -= num_bytes; |
| } |
| |
| static boolean _jpeg_resync_to_restart(j_decompress_ptr cinfo, int desired) { |
| ERR("(desired=%d), should not get here.\n",desired); |
| return FALSE; |
| } |
| static void _jpeg_term_source(j_decompress_ptr cinfo) { } |
| #endif |
| |
| /* DG_IMAGE/DAT_CIECOLOR/MSG_GET */ |
| TW_UINT16 GPHOTO2_CIEColorGet (pTW_IDENTITY pOrigin, |
| TW_MEMREF pData) |
| { |
| FIXME ("stub!\n"); |
| |
| return TWRC_FAILURE; |
| } |
| |
| /* DG_IMAGE/DAT_EXTIMAGEINFO/MSG_GET */ |
| TW_UINT16 GPHOTO2_ExtImageInfoGet (pTW_IDENTITY pOrigin, |
| TW_MEMREF pData) |
| { |
| FIXME ("stub!\n"); |
| |
| return TWRC_FAILURE; |
| } |
| |
| /* DG_IMAGE/DAT_GRAYRESPONSE/MSG_RESET */ |
| TW_UINT16 GPHOTO2_GrayResponseReset (pTW_IDENTITY pOrigin, |
| TW_MEMREF pData) |
| { |
| FIXME ("stub!\n"); |
| |
| return TWRC_FAILURE; |
| } |
| |
| /* DG_IMAGE/DAT_GRAYRESPONSE/MSG_SET */ |
| TW_UINT16 GPHOTO2_GrayResponseSet (pTW_IDENTITY pOrigin, |
| TW_MEMREF pData) |
| { |
| FIXME ("stub!\n"); |
| |
| return TWRC_FAILURE; |
| } |
| |
| /* DG_IMAGE/DAT_IMAGEFILEXFER/MSG_GET */ |
| TW_UINT16 GPHOTO2_ImageFileXferGet (pTW_IDENTITY pOrigin, |
| TW_MEMREF pData) |
| { |
| FIXME ("stub!\n"); |
| |
| return TWRC_FAILURE; |
| } |
| |
| #ifdef HAVE_GPHOTO2 |
| static TW_UINT16 _get_image_and_startup_jpeg(void) { |
| const char *folder = NULL, *filename = NULL; |
| struct gphoto2_file *file; |
| const unsigned char *filedata; |
| unsigned long filesize; |
| int ret; |
| |
| if (activeDS.file) /* Already loaded. */ |
| return TWRC_SUCCESS; |
| |
| if(!libjpeg_handle) { |
| if(!load_libjpeg()) { |
| FIXME("Failed reading JPEG because unable to find %s\n", SONAME_LIBJPEG); |
| filedata = NULL; |
| return TWRC_FAILURE; |
| } |
| } |
| |
| LIST_FOR_EACH_ENTRY( file, &activeDS.files, struct gphoto2_file, entry ) { |
| if (strstr(file->filename,".JPG") || strstr(file->filename,".jpg")) { |
| filename = file->filename; |
| folder = file->folder; |
| TRACE("downloading %s/%s\n", folder, filename); |
| if (file->download) { |
| file->download = FALSE; /* mark as done */ |
| break; |
| } |
| } |
| } |
| gp_file_new (&activeDS.file); |
| ret = gp_camera_file_get(activeDS.camera, folder, filename, GP_FILE_TYPE_NORMAL, |
| activeDS.file, activeDS.context); |
| if (ret < GP_OK) { |
| FIXME("Failed to get file?\n"); |
| activeDS.twCC = TWCC_SEQERROR; |
| return TWRC_FAILURE; |
| } |
| ret = gp_file_get_data_and_size (activeDS.file, (const char**)&filedata, &filesize); |
| if (ret < GP_OK) { |
| FIXME("Failed to get file data?\n"); |
| activeDS.twCC = TWCC_SEQERROR; |
| return TWRC_FAILURE; |
| } |
| |
| /* This is basically so we can use in-memory data for jpeg decompression. |
| * We need to have all the functions. |
| */ |
| activeDS.xjsm.next_input_byte = filedata; |
| activeDS.xjsm.bytes_in_buffer = filesize; |
| activeDS.xjsm.init_source = _jpeg_init_source; |
| activeDS.xjsm.fill_input_buffer = _jpeg_fill_input_buffer; |
| activeDS.xjsm.skip_input_data = _jpeg_skip_input_data; |
| activeDS.xjsm.resync_to_restart = _jpeg_resync_to_restart; |
| activeDS.xjsm.term_source = _jpeg_term_source; |
| |
| activeDS.jd.err = pjpeg_std_error(&activeDS.jerr); |
| /* jpeg_create_decompress is a macro that expands to jpeg_CreateDecompress - see jpeglib.h |
| * jpeg_create_decompress(&jd); */ |
| pjpeg_CreateDecompress(&activeDS.jd, JPEG_LIB_VERSION, (size_t) sizeof(struct jpeg_decompress_struct)); |
| activeDS.jd.src = &activeDS.xjsm; |
| ret=pjpeg_read_header(&activeDS.jd,TRUE); |
| activeDS.jd.out_color_space = JCS_RGB; |
| pjpeg_start_decompress(&activeDS.jd); |
| if (ret != JPEG_HEADER_OK) { |
| ERR("Jpeg image in stream has bad format, read header returned %d.\n",ret); |
| gp_file_unref (activeDS.file); |
| activeDS.file = NULL; |
| return TWRC_FAILURE; |
| } |
| return TWRC_SUCCESS; |
| } |
| #endif |
| |
| /* DG_IMAGE/DAT_IMAGEINFO/MSG_GET */ |
| TW_UINT16 GPHOTO2_ImageInfoGet (pTW_IDENTITY pOrigin, |
| TW_MEMREF pData) |
| { |
| #ifdef HAVE_GPHOTO2 |
| pTW_IMAGEINFO pImageInfo = (pTW_IMAGEINFO) pData; |
| |
| TRACE("DG_IMAGE/DAT_IMAGEINFO/MSG_GET\n"); |
| |
| if (activeDS.currentState != 6 && activeDS.currentState != 7) { |
| activeDS.twCC = TWCC_SEQERROR; |
| return TWRC_FAILURE; |
| } |
| if (TWRC_SUCCESS != _get_image_and_startup_jpeg()) { |
| FIXME("Failed to get an image\n"); |
| activeDS.twCC = TWCC_SEQERROR; |
| return TWRC_FAILURE; |
| } |
| if (activeDS.currentState == 6) |
| { |
| /* return general image description information about the image about to be transferred */ |
| TRACE("Getting parameters\n"); |
| } |
| TRACE("activeDS.jd.output_width = %d\n", activeDS.jd.output_width); |
| TRACE("activeDS.jd.output_height = %d\n", activeDS.jd.output_height); |
| pImageInfo->Compression = TWCP_NONE; |
| pImageInfo->SamplesPerPixel = 3; |
| pImageInfo->BitsPerSample[0]= 8; |
| pImageInfo->BitsPerSample[1]= 8; |
| pImageInfo->BitsPerSample[2]= 8; |
| pImageInfo->PixelType = TWPT_RGB; |
| pImageInfo->Planar = FALSE; /* R-G-B is chunky! */ |
| pImageInfo->XResolution.Whole = -1; |
| pImageInfo->XResolution.Frac = 0; |
| pImageInfo->YResolution.Whole = -1; |
| pImageInfo->YResolution.Frac = 0; |
| pImageInfo->ImageWidth = activeDS.jd.output_width; |
| pImageInfo->ImageLength = activeDS.jd.output_height; |
| pImageInfo->BitsPerPixel = 24; |
| return TWRC_SUCCESS; |
| #else |
| return TWRC_FAILURE; |
| #endif |
| } |
| |
| /* DG_IMAGE/DAT_IMAGELAYOUT/MSG_GET */ |
| TW_UINT16 GPHOTO2_ImageLayoutGet (pTW_IDENTITY pOrigin, |
| TW_MEMREF pData) |
| { |
| FIXME ("stub!\n"); |
| |
| return TWRC_FAILURE; |
| } |
| |
| /* DG_IMAGE/DAT_IMAGELAYOUT/MSG_GETDEFAULT */ |
| TW_UINT16 GPHOTO2_ImageLayoutGetDefault (pTW_IDENTITY pOrigin, |
| TW_MEMREF pData) |
| { |
| FIXME ("stub!\n"); |
| |
| return TWRC_FAILURE; |
| } |
| |
| /* DG_IMAGE/DAT_IMAGELAYOUT/MSG_RESET */ |
| TW_UINT16 GPHOTO2_ImageLayoutReset (pTW_IDENTITY pOrigin, |
| TW_MEMREF pData) |
| { |
| FIXME ("stub!\n"); |
| |
| return TWRC_FAILURE; |
| } |
| |
| /* DG_IMAGE/DAT_IMAGELAYOUT/MSG_SET */ |
| TW_UINT16 GPHOTO2_ImageLayoutSet (pTW_IDENTITY pOrigin, |
| TW_MEMREF pData) |
| { |
| FIXME ("stub!\n"); |
| |
| return TWRC_FAILURE; |
| } |
| |
| /* DG_IMAGE/DAT_IMAGEMEMXFER/MSG_GET */ |
| TW_UINT16 GPHOTO2_ImageMemXferGet (pTW_IDENTITY pOrigin, |
| TW_MEMREF pData) |
| { |
| #ifdef HAVE_GPHOTO2 |
| TW_UINT16 twRC = TWRC_SUCCESS; |
| pTW_IMAGEMEMXFER pImageMemXfer = (pTW_IMAGEMEMXFER) pData; |
| LPBYTE buffer; |
| int readrows; |
| unsigned int curoff; |
| |
| TRACE ("DG_IMAGE/DAT_IMAGEMEMXFER/MSG_GET\n"); |
| if (activeDS.currentState < 6 || activeDS.currentState > 7) { |
| activeDS.twCC = TWCC_SEQERROR; |
| return TWRC_FAILURE; |
| } |
| TRACE("pImageMemXfer.Compression is %d\n", pImageMemXfer->Compression); |
| if (activeDS.currentState == 6) { |
| if (TWRC_SUCCESS != _get_image_and_startup_jpeg()) { |
| FIXME("Failed to get an image\n"); |
| activeDS.twCC = TWCC_SEQERROR; |
| return TWRC_FAILURE; |
| } |
| |
| if (!activeDS.progressWnd) |
| activeDS.progressWnd = TransferringDialogBox(NULL,0); |
| TransferringDialogBox(activeDS.progressWnd,0); |
| |
| activeDS.currentState = 7; |
| } else { |
| if (!activeDS.file) { |
| activeDS.twCC = TWRC_SUCCESS; |
| return TWRC_XFERDONE; |
| } |
| } |
| |
| if (pImageMemXfer->Memory.Flags & TWMF_HANDLE) { |
| FIXME("Memory Handle, may not be locked correctly\n"); |
| buffer = LocalLock(pImageMemXfer->Memory.TheMem); |
| } else |
| buffer = pImageMemXfer->Memory.TheMem; |
| |
| memset(buffer,0,pImageMemXfer->Memory.Length); |
| curoff = 0; readrows = 0; |
| pImageMemXfer->YOffset = activeDS.jd.output_scanline; |
| pImageMemXfer->XOffset = 0; /* we do whole strips */ |
| while ((activeDS.jd.output_scanline<activeDS.jd.output_height) && |
| ((pImageMemXfer->Memory.Length - curoff) > activeDS.jd.output_width*activeDS.jd.output_components) |
| ) { |
| JSAMPROW row = buffer+curoff; |
| int x = pjpeg_read_scanlines(&activeDS.jd,&row,1); |
| if (x != 1) { |
| FIXME("failed to read current scanline?\n"); |
| break; |
| } |
| readrows++; |
| curoff += activeDS.jd.output_width*activeDS.jd.output_components; |
| } |
| pImageMemXfer->Compression = TWCP_NONE; |
| pImageMemXfer->BytesPerRow = activeDS.jd.output_components * activeDS.jd.output_width; |
| pImageMemXfer->Rows = readrows; |
| pImageMemXfer->Columns = activeDS.jd.output_width; /* we do whole strips */ |
| pImageMemXfer->BytesWritten = curoff; |
| |
| TransferringDialogBox(activeDS.progressWnd,0); |
| |
| if (activeDS.jd.output_scanline == activeDS.jd.output_height) { |
| pjpeg_finish_decompress(&activeDS.jd); |
| pjpeg_destroy_decompress(&activeDS.jd); |
| gp_file_unref (activeDS.file); |
| activeDS.file = NULL; |
| TRACE("xfer is done!\n"); |
| |
| /*TransferringDialogBox(activeDS.progressWnd, -1);*/ |
| twRC = TWRC_XFERDONE; |
| } |
| activeDS.twCC = TWRC_SUCCESS; |
| if (pImageMemXfer->Memory.Flags & TWMF_HANDLE) |
| LocalUnlock(pImageMemXfer->Memory.TheMem); |
| return twRC; |
| #else |
| return TWRC_FAILURE; |
| #endif |
| } |
| |
| /* DG_IMAGE/DAT_IMAGENATIVEXFER/MSG_GET */ |
| TW_UINT16 GPHOTO2_ImageNativeXferGet (pTW_IDENTITY pOrigin, |
| TW_MEMREF pData) |
| { |
| #ifdef HAVE_GPHOTO2 |
| pTW_UINT32 pHandle = (pTW_UINT32) pData; |
| HBITMAP hDIB; |
| BITMAPINFO bmpInfo; |
| LPBYTE bits; |
| JSAMPROW samprow, oldsamprow; |
| |
| FIXME("DG_IMAGE/DAT_IMAGENATIVEXFER/MSG_GET: implemented, but expect program crash due to DIB.\n"); |
| |
| /* NOTE NOTE NOTE NOTE NOTE NOTE NOTE |
| * |
| * While this is a mandatory transfer mode and this function |
| * is correctly implemented and fully works, the calling program |
| * will likely crash after calling. |
| * |
| * Reason is that there is a lot of example code that does: |
| * bmpinfo = GlobalLock(hBITMAP); ... pointer access to bmpinfo |
| * |
| * Our current HBITMAP handles do not support getting GlobalLocked -> App Crash |
| * |
| * This needs a GDI Handle rewrite, at least for DIB sections. |
| * - Marcus |
| */ |
| if (activeDS.currentState != 6) { |
| activeDS.twCC = TWCC_SEQERROR; |
| return TWRC_FAILURE; |
| } |
| if (TWRC_SUCCESS != _get_image_and_startup_jpeg()) { |
| FIXME("Failed to get an image\n"); |
| activeDS.twCC = TWCC_OPERATIONERROR; |
| return TWRC_FAILURE; |
| } |
| TRACE("Acquiring image %dx%dx%d bits from gphoto.\n", |
| activeDS.jd.output_width, activeDS.jd.output_height, |
| activeDS.jd.output_components*8); |
| ZeroMemory (&bmpInfo, sizeof (BITMAPINFO)); |
| bmpInfo.bmiHeader.biSize = sizeof (BITMAPINFOHEADER); |
| bmpInfo.bmiHeader.biWidth = activeDS.jd.output_width; |
| bmpInfo.bmiHeader.biHeight = -activeDS.jd.output_height; |
| bmpInfo.bmiHeader.biPlanes = 1; |
| bmpInfo.bmiHeader.biBitCount = activeDS.jd.output_components*8; |
| bmpInfo.bmiHeader.biCompression = BI_RGB; |
| bmpInfo.bmiHeader.biSizeImage = 0; |
| bmpInfo.bmiHeader.biXPelsPerMeter = 0; |
| bmpInfo.bmiHeader.biYPelsPerMeter = 0; |
| bmpInfo.bmiHeader.biClrUsed = 0; |
| bmpInfo.bmiHeader.biClrImportant = 0; |
| hDIB = CreateDIBSection (0, &bmpInfo, DIB_RGB_COLORS, (LPVOID)&bits, 0, 0); |
| if (!hDIB) { |
| FIXME("Failed creating DIB.\n"); |
| gp_file_unref (activeDS.file); |
| activeDS.file = NULL; |
| activeDS.twCC = TWCC_LOWMEMORY; |
| return TWRC_FAILURE; |
| } |
| samprow = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,activeDS.jd.output_width*activeDS.jd.output_components); |
| oldsamprow = samprow; |
| while ( activeDS.jd.output_scanline<activeDS.jd.output_height ) { |
| unsigned int i; |
| int x = pjpeg_read_scanlines(&activeDS.jd,&samprow,1); |
| if (x != 1) { |
| FIXME("failed to read current scanline?\n"); |
| break; |
| } |
| /* We have to convert from RGB to BGR, see MSDN/ BITMAPINFOHEADER */ |
| for(i=0;i<activeDS.jd.output_width;i++,samprow+=activeDS.jd.output_components) { |
| *(bits++) = *(samprow+2); |
| *(bits++) = *(samprow+1); |
| *(bits++) = *(samprow); |
| } |
| bits = (LPBYTE)(((UINT_PTR)bits + 3) & ~3); |
| samprow = oldsamprow; |
| } |
| HeapFree (GetProcessHeap(), 0, samprow); |
| gp_file_unref (activeDS.file); |
| activeDS.file = NULL; |
| *pHandle = (UINT_PTR)hDIB; |
| activeDS.twCC = TWCC_SUCCESS; |
| activeDS.currentState = 7; |
| return TWRC_XFERDONE; |
| #else |
| return TWRC_FAILURE; |
| #endif |
| } |
| |
| /* DG_IMAGE/DAT_JPEGCOMPRESSION/MSG_GET */ |
| TW_UINT16 GPHOTO2_JPEGCompressionGet (pTW_IDENTITY pOrigin, |
| TW_MEMREF pData) |
| { |
| FIXME ("stub!\n"); |
| |
| return TWRC_FAILURE; |
| } |
| |
| /* DG_IMAGE/DAT_JPEGCOMPRESSION/MSG_GETDEFAULT */ |
| TW_UINT16 GPHOTO2_JPEGCompressionGetDefault (pTW_IDENTITY pOrigin, |
| |
| TW_MEMREF pData) |
| { |
| FIXME ("stub!\n"); |
| |
| return TWRC_FAILURE; |
| } |
| |
| /* DG_IMAGE/DAT_JPEGCOMPRESSION/MSG_RESET */ |
| TW_UINT16 GPHOTO2_JPEGCompressionReset (pTW_IDENTITY pOrigin, |
| TW_MEMREF pData) |
| { |
| FIXME ("stub!\n"); |
| |
| return TWRC_FAILURE; |
| } |
| |
| /* DG_IMAGE/DAT_JPEGCOMPRESSION/MSG_SET */ |
| TW_UINT16 GPHOTO2_JPEGCompressionSet (pTW_IDENTITY pOrigin, |
| TW_MEMREF pData) |
| { |
| FIXME ("stub!\n"); |
| |
| return TWRC_FAILURE; |
| } |
| |
| /* DG_IMAGE/DAT_PALETTE8/MSG_GET */ |
| TW_UINT16 GPHOTO2_Palette8Get (pTW_IDENTITY pOrigin, |
| TW_MEMREF pData) |
| { |
| FIXME ("stub!\n"); |
| |
| return TWRC_FAILURE; |
| } |
| |
| /* DG_IMAGE/DAT_PALETTE8/MSG_GETDEFAULT */ |
| TW_UINT16 GPHOTO2_Palette8GetDefault (pTW_IDENTITY pOrigin, |
| TW_MEMREF pData) |
| { |
| FIXME ("stub!\n"); |
| |
| return TWRC_FAILURE; |
| } |
| |
| /* DG_IMAGE/DAT_PALETTE8/MSG_RESET */ |
| TW_UINT16 GPHOTO2_Palette8Reset (pTW_IDENTITY pOrigin, |
| TW_MEMREF pData) |
| { |
| FIXME ("stub!\n"); |
| |
| return TWRC_FAILURE; |
| } |
| |
| /* DG_IMAGE/DAT_PALETTE8/MSG_SET */ |
| TW_UINT16 GPHOTO2_Palette8Set (pTW_IDENTITY pOrigin, |
| TW_MEMREF pData) |
| { |
| FIXME ("stub!\n"); |
| |
| return TWRC_FAILURE; |
| } |
| |
| /* DG_IMAGE/DAT_RGBRESPONSE/MSG_RESET */ |
| TW_UINT16 GPHOTO2_RGBResponseReset (pTW_IDENTITY pOrigin, |
| TW_MEMREF pData) |
| { |
| FIXME ("stub!\n"); |
| |
| return TWRC_FAILURE; |
| } |
| |
| /* DG_IMAGE/DAT_RGBRESPONSE/MSG_SET */ |
| TW_UINT16 GPHOTO2_RGBResponseSet (pTW_IDENTITY pOrigin, |
| TW_MEMREF pData) |
| { |
| FIXME ("stub!\n"); |
| |
| return TWRC_FAILURE; |
| } |
| |
| #ifdef HAVE_GPHOTO2 |
| TW_UINT16 |
| _get_gphoto2_file_as_DIB( |
| const char *folder, const char *filename, CameraFileType type, |
| HWND hwnd, HBITMAP *hDIB |
| ) { |
| const unsigned char *filedata; |
| unsigned long filesize; |
| int ret; |
| CameraFile *file; |
| struct jpeg_source_mgr xjsm; |
| struct jpeg_decompress_struct jd; |
| struct jpeg_error_mgr jerr; |
| BITMAPINFO bmpInfo; |
| LPBYTE bits; |
| JSAMPROW samprow, oldsamprow; |
| |
| if(!libjpeg_handle) { |
| if(!load_libjpeg()) { |
| FIXME("Failed reading JPEG because unable to find %s\n", SONAME_LIBJPEG); |
| filedata = NULL; |
| return TWRC_FAILURE; |
| } |
| } |
| |
| gp_file_new (&file); |
| ret = gp_camera_file_get(activeDS.camera, folder, filename, type, file, activeDS.context); |
| if (ret < GP_OK) { |
| FIXME("Failed to get file?\n"); |
| gp_file_unref (file); |
| return TWRC_FAILURE; |
| } |
| ret = gp_file_get_data_and_size (file, (const char**)&filedata, &filesize); |
| if (ret < GP_OK) { |
| FIXME("Failed to get file data?\n"); |
| return TWRC_FAILURE; |
| } |
| |
| /* FIXME: Actually we might get other types than JPEG ... But only handle JPEG for now */ |
| if (filedata[0] != 0xff) { |
| ERR("File %s/%s might not be JPEG, cannot decode!\n", folder, filename); |
| } |
| |
| /* This is basically so we can use in-memory data for jpeg decompression. |
| * We need to have all the functions. |
| */ |
| xjsm.next_input_byte = filedata; |
| xjsm.bytes_in_buffer = filesize; |
| xjsm.init_source = _jpeg_init_source; |
| xjsm.fill_input_buffer = _jpeg_fill_input_buffer; |
| xjsm.skip_input_data = _jpeg_skip_input_data; |
| xjsm.resync_to_restart = _jpeg_resync_to_restart; |
| xjsm.term_source = _jpeg_term_source; |
| |
| jd.err = pjpeg_std_error(&jerr); |
| /* jpeg_create_decompress is a macro that expands to jpeg_CreateDecompress - see jpeglib.h |
| * jpeg_create_decompress(&jd); */ |
| pjpeg_CreateDecompress(&jd, JPEG_LIB_VERSION, (size_t) sizeof(struct jpeg_decompress_struct)); |
| jd.src = &xjsm; |
| ret=pjpeg_read_header(&jd,TRUE); |
| jd.out_color_space = JCS_RGB; |
| pjpeg_start_decompress(&jd); |
| if (ret != JPEG_HEADER_OK) { |
| ERR("Jpeg image in stream has bad format, read header returned %d.\n",ret); |
| gp_file_unref (file); |
| return TWRC_FAILURE; |
| } |
| |
| ZeroMemory (&bmpInfo, sizeof (BITMAPINFO)); |
| bmpInfo.bmiHeader.biSize = sizeof (BITMAPINFOHEADER); |
| bmpInfo.bmiHeader.biWidth = jd.output_width; |
| bmpInfo.bmiHeader.biHeight = -jd.output_height; |
| bmpInfo.bmiHeader.biPlanes = 1; |
| bmpInfo.bmiHeader.biBitCount = jd.output_components*8; |
| bmpInfo.bmiHeader.biCompression = BI_RGB; |
| bmpInfo.bmiHeader.biSizeImage = 0; |
| bmpInfo.bmiHeader.biXPelsPerMeter = 0; |
| bmpInfo.bmiHeader.biYPelsPerMeter = 0; |
| bmpInfo.bmiHeader.biClrUsed = 0; |
| bmpInfo.bmiHeader.biClrImportant = 0; |
| *hDIB = CreateDIBSection(0, &bmpInfo, DIB_RGB_COLORS, (LPVOID)&bits, 0, 0); |
| if (!*hDIB) { |
| FIXME("Failed creating DIB.\n"); |
| gp_file_unref (file); |
| return TWRC_FAILURE; |
| } |
| samprow = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,jd.output_width*jd.output_components); |
| oldsamprow = samprow; |
| while ( jd.output_scanline<jd.output_height ) { |
| unsigned int i; |
| int x = pjpeg_read_scanlines(&jd,&samprow,1); |
| if (x != 1) { |
| FIXME("failed to read current scanline?\n"); |
| break; |
| } |
| /* We have to convert from RGB to BGR, see MSDN/ BITMAPINFOHEADER */ |
| for(i=0;i<jd.output_width;i++,samprow+=jd.output_components) { |
| *(bits++) = *(samprow+2); |
| *(bits++) = *(samprow+1); |
| *(bits++) = *(samprow); |
| } |
| bits = (LPBYTE)(((UINT_PTR)bits + 3) & ~3); |
| samprow = oldsamprow; |
| } |
| HeapFree (GetProcessHeap(), 0, samprow); |
| gp_file_unref (file); |
| return TWRC_SUCCESS; |
| } |
| #endif |