FIBITMAP* psdParser::ReadImageData(FreeImageIO *io, fi_handle handle) { if(handle == NULL) return NULL; bool header_only = (_fi_flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS; WORD nCompression = 0; io->read_proc(&nCompression, sizeof(nCompression), 1, handle); #ifndef FREEIMAGE_BIGENDIAN SwapShort(&nCompression); #endif if((nCompression != PSDP_COMPRESSION_NONE && nCompression != PSDP_COMPRESSION_RLE)) { FreeImage_OutputMessageProc(_fi_format_id, "Unsupported compression %d", nCompression); return NULL; } const unsigned nWidth = _headerInfo._Width; const unsigned nHeight = _headerInfo._Height; const unsigned nChannels = _headerInfo._Channels; const unsigned depth = _headerInfo._BitsPerChannel; const unsigned bytes = (depth == 1) ? 1 : depth / 8; // channel(plane) line (BYTE aligned) const unsigned lineSize = (_headerInfo._BitsPerChannel == 1) ? (nWidth + 7) / 8 : nWidth * bytes; if(nCompression == PSDP_COMPRESSION_RLE && depth > 16) { FreeImage_OutputMessageProc(_fi_format_id, "Unsupported RLE with depth %d", depth); return NULL; } // build output buffer FIBITMAP* bitmap = NULL; unsigned dstCh = 0; short mode = _headerInfo._ColourMode; if(mode == PSDP_MULTICHANNEL && nChannels < 3) { // CM mode = PSDP_GRAYSCALE; // C as gray, M as extra channel } bool needPalette = false; switch (mode) { case PSDP_BITMAP: case PSDP_DUOTONE: case PSDP_INDEXED: case PSDP_GRAYSCALE: dstCh = 1; switch(depth) { case 16: bitmap = FreeImage_AllocateHeaderT(header_only, FIT_UINT16, nWidth, nHeight, depth*dstCh); break; case 32: bitmap = FreeImage_AllocateHeaderT(header_only, FIT_FLOAT, nWidth, nHeight, depth*dstCh); break; default: // 1-, 8- needPalette = true; bitmap = FreeImage_AllocateHeader(header_only, nWidth, nHeight, depth*dstCh); break; } break; case PSDP_RGB: case PSDP_LAB: case PSDP_CMYK : case PSDP_MULTICHANNEL : // force PSDP_MULTICHANNEL CMY as CMYK dstCh = (mode == PSDP_MULTICHANNEL && !header_only) ? 4 : MIN<unsigned>(nChannels, 4); if(dstCh < 3) { throw "Invalid number of channels"; } switch(depth) { case 16: bitmap = FreeImage_AllocateHeaderT(header_only, dstCh < 4 ? FIT_RGB16 : FIT_RGBA16, nWidth, nHeight, depth*dstCh); break; case 32: bitmap = FreeImage_AllocateHeaderT(header_only, dstCh < 4 ? FIT_RGBF : FIT_RGBAF, nWidth, nHeight, depth*dstCh); break; default: bitmap = FreeImage_AllocateHeader(header_only, nWidth, nHeight, depth*dstCh); break; } break; default: throw "Unsupported color mode"; break; } if(!bitmap) { throw FI_MSG_ERROR_DIB_MEMORY; } // write thumbnail FreeImage_SetThumbnail(bitmap, _thumbnail.getDib()); // @todo Add some metadata model if(header_only) { return bitmap; } // Load pixels data const unsigned dstChannels = dstCh; const unsigned dstBpp = (depth == 1) ? 1 : FreeImage_GetBPP(bitmap)/8; const unsigned dstLineSize = FreeImage_GetPitch(bitmap); BYTE* const dst_first_line = FreeImage_GetScanLine(bitmap, nHeight - 1);//<*** flipped BYTE* line_start = new BYTE[lineSize]; //< fileline cache switch ( nCompression ) { case PSDP_COMPRESSION_NONE: // raw data { for(unsigned c = 0; c < nChannels; c++) { if(c >= dstChannels) { // @todo write extra channels break; } const unsigned channelOffset = c * bytes; BYTE* dst_line_start = dst_first_line; for(unsigned h = 0; h < nHeight; ++h, dst_line_start -= dstLineSize) {//<*** flipped io->read_proc(line_start, lineSize, 1, handle); for (BYTE *line = line_start, *dst_line = dst_line_start; line < line_start + lineSize; line += bytes, dst_line += dstBpp) { #ifdef FREEIMAGE_BIGENDIAN memcpy(dst_line + channelOffset, line, bytes); #else // reverse copy bytes for (unsigned b = 0; b < bytes; ++b) { dst_line[channelOffset + b] = line[(bytes-1) - b]; } #endif // FREEIMAGE_BIGENDIAN } } //< h }//< ch SAFE_DELETE_ARRAY(line_start); } break; case PSDP_COMPRESSION_RLE: // RLE compression { // The RLE-compressed data is preceeded by a 2-byte line size for each row in the data, // store an array of these // later use this array as WORD rleLineSizeList[nChannels][nHeight]; WORD *rleLineSizeList = new (std::nothrow) WORD[nChannels*nHeight]; if(!rleLineSizeList) { FreeImage_Unload(bitmap); SAFE_DELETE_ARRAY(line_start); throw std::bad_alloc(); } io->read_proc(rleLineSizeList, 2, nChannels * nHeight, handle); WORD largestRLELine = 0; for(unsigned ch = 0; ch < nChannels; ++ch) { for(unsigned h = 0; h < nHeight; ++h) { const unsigned index = ch * nHeight + h; #ifndef FREEIMAGE_BIGENDIAN SwapShort(&rleLineSizeList[index]); #endif if(largestRLELine < rleLineSizeList[index]) { largestRLELine = rleLineSizeList[index]; } } } BYTE* rle_line_start = new (std::nothrow) BYTE[largestRLELine]; if(!rle_line_start) { FreeImage_Unload(bitmap); SAFE_DELETE_ARRAY(line_start); SAFE_DELETE_ARRAY(rleLineSizeList); throw std::bad_alloc(); } // Read the RLE data (assume 8-bit) const BYTE* const line_end = line_start + lineSize; for (unsigned ch = 0; ch < nChannels; ch++) { const unsigned channelOffset = ch * bytes; BYTE* dst_line_start = dst_first_line; for(unsigned h = 0; h < nHeight; ++h, dst_line_start -= dstLineSize) {//<*** flipped const unsigned index = ch * nHeight + h; // - read and uncompress line - const WORD rleLineSize = rleLineSizeList[index]; io->read_proc(rle_line_start, rleLineSize, 1, handle); for (BYTE* rle_line = rle_line_start, *line = line_start; rle_line < rle_line_start + rleLineSize, line < line_end;) { int len = *rle_line++; // NOTE len is signed byte in PackBits RLE if ( len < 128 ) { //<- MSB is not set // uncompressed packet // (len + 1) bytes of data are copied ++len; // assert we don't write beyound eol memcpy(line, rle_line, line + len > line_end ? line_end - line : len); line += len; rle_line += len; } else if ( len > 128 ) { //< MSB is set // RLE compressed packet // One byte of data is repeated (–len + 1) times len ^= 0xFF; // same as (-len + 1) & 0xFF len += 2; // // assert we don't write beyound eol memset(line, *rle_line++, line + len > line_end ? line_end - line : len); line += len; } else if ( 128 == len ) { // Do nothing } }//< rle_line // - write line to destination - if(ch >= dstChannels) { // @todo write to extra channels break; } // byte by byte copy a single channel to pixel for (BYTE *line = line_start, *dst_line = dst_line_start; line < line_start + lineSize; line += bytes, dst_line += dstBpp) { #ifdef FREEIMAGE_BIGENDIAN memcpy(dst_line + channelOffset, line, bytes); #else // reverse copy bytes for (unsigned b = 0; b < bytes; ++b) { dst_line[channelOffset + b] = line[(bytes-1) - b]; } #endif // FREEIMAGE_BIGENDIAN } }//< h }//< ch SAFE_DELETE_ARRAY(line_start); SAFE_DELETE_ARRAY(rleLineSizeList); SAFE_DELETE_ARRAY(rle_line_start); } break; case 2: // ZIP without prediction, no specification break; case 3: // ZIP with prediction, no specification break; default: // Unknown format break; } // --- Further process the bitmap --- if((mode == PSDP_CMYK || mode == PSDP_MULTICHANNEL)) { // CMYK values are "inverted", invert them back if(mode == PSDP_MULTICHANNEL) { invertColor(bitmap); } else { FreeImage_Invert(bitmap); } if((_fi_flags & PSD_CMYK) == PSD_CMYK) { // keep as CMYK if(mode == PSDP_MULTICHANNEL) { //### we force CMY to be CMYK, but CMY has no ICC. // Create empty profile and add the flag. FreeImage_CreateICCProfile(bitmap, NULL, 0); FreeImage_GetICCProfile(bitmap)->flags |= FIICC_COLOR_IS_CMYK; } } else { // convert to RGB ConvertCMYKtoRGBA(bitmap); // The ICC Profile is no longer valid _iccProfile.clear(); // remove the pending A if not present in source if(nChannels == 4 || nChannels == 3 ) { FIBITMAP* t = RemoveAlphaChannel(bitmap); if(t) { FreeImage_Unload(bitmap); bitmap = t; } // else: silently fail } } } else if ( mode == PSDP_LAB && !((_fi_flags & PSD_LAB) == PSD_LAB)) { ConvertLABtoRGB(bitmap); } else { if (needPalette && FreeImage_GetPalette(bitmap)) { if(mode == PSDP_BITMAP) { CREATE_GREYSCALE_PALETTE_REVERSE(FreeImage_GetPalette(bitmap), 2); } else if(mode == PSDP_INDEXED) { if(!_colourModeData._plColourData || _colourModeData._Length != 768 || _ColourCount < 0) { FreeImage_OutputMessageProc(_fi_format_id, "Indexed image has no palette. Using the default grayscale one."); } else { _colourModeData.FillPalette(bitmap); } } // GRAYSCALE, DUOTONE - use default grayscale palette } #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR if(FreeImage_GetImageType(bitmap) == FIT_BITMAP) { SwapRedBlue32(bitmap); } #endif } return bitmap; }
static FIBITMAP * DLL_CALLCONV Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { FIBITMAP *dib = NULL; BYTE *bits; // Pointer to dib data RGBQUAD *pal; // Pointer to dib palette BYTE *line = NULL; // PCX raster line BYTE *ReadBuf = NULL; // buffer; BOOL bIsRLE; // True if the file is run-length encoded if(!handle) { return NULL; } BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS; try { // check PCX identifier long start_pos = io->tell_proc(handle); BOOL validated = pcx_validate(io, handle); io->seek_proc(handle, start_pos, SEEK_SET); if(!validated) { throw FI_MSG_ERROR_MAGIC_NUMBER; } // process the header PCXHEADER header; if(io->read_proc(&header, sizeof(PCXHEADER), 1, handle) != 1) { throw FI_MSG_ERROR_PARSING; } #ifdef FREEIMAGE_BIGENDIAN SwapHeader(&header); #endif // allocate a new DIB unsigned width = header.window[2] - header.window[0] + 1; unsigned height = header.window[3] - header.window[1] + 1; unsigned bitcount = header.bpp * header.planes; if (bitcount == 24) { dib = FreeImage_AllocateHeader(header_only, width, height, bitcount, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); } else { dib = FreeImage_AllocateHeader(header_only, width, height, bitcount); } // if the dib couldn't be allocated, throw an error if (!dib) { throw FI_MSG_ERROR_DIB_MEMORY; } // metrics handling code FreeImage_SetDotsPerMeterX(dib, (unsigned) (((float)header.hdpi) / 0.0254000 + 0.5)); FreeImage_SetDotsPerMeterY(dib, (unsigned) (((float)header.vdpi) / 0.0254000 + 0.5)); // Set up the palette if needed // ---------------------------- switch(bitcount) { case 1: { pal = FreeImage_GetPalette(dib); pal[0].rgbRed = pal[0].rgbGreen = pal[0].rgbBlue = 0; pal[1].rgbRed = pal[1].rgbGreen = pal[1].rgbBlue = 255; break; } case 4: { pal = FreeImage_GetPalette(dib); BYTE *pColormap = &header.color_map[0]; for (int i = 0; i < 16; i++) { pal[i].rgbRed = pColormap[0]; pal[i].rgbGreen = pColormap[1]; pal[i].rgbBlue = pColormap[2]; pColormap += 3; } break; } case 8: { BYTE palette_id; io->seek_proc(handle, -769L, SEEK_END); io->read_proc(&palette_id, 1, 1, handle); if (palette_id == 0x0C) { BYTE *cmap = (BYTE*)malloc(768 * sizeof(BYTE)); io->read_proc(cmap, 768, 1, handle); pal = FreeImage_GetPalette(dib); BYTE *pColormap = &cmap[0]; for(int i = 0; i < 256; i++) { pal[i].rgbRed = pColormap[0]; pal[i].rgbGreen = pColormap[1]; pal[i].rgbBlue = pColormap[2]; pColormap += 3; } free(cmap); } // wrong palette ID, perhaps a gray scale is needed ? else if (header.palette_info == 2) { pal = FreeImage_GetPalette(dib); for(int i = 0; i < 256; i++) { pal[i].rgbRed = (BYTE)i; pal[i].rgbGreen = (BYTE)i; pal[i].rgbBlue = (BYTE)i; } } io->seek_proc(handle, (long)sizeof(PCXHEADER), SEEK_SET); } break; } if(header_only) { // header only mode return dib; } // calculate the line length for the PCX and the DIB // length of raster line in bytes unsigned linelength = header.bytes_per_line * header.planes; // length of DIB line (rounded to DWORD) in bytes unsigned pitch = FreeImage_GetPitch(dib); // run-length encoding ? bIsRLE = (header.encoding == 1) ? TRUE : FALSE; // load image data // --------------- line = (BYTE*)malloc(linelength * sizeof(BYTE)); if(!line) throw FI_MSG_ERROR_MEMORY; ReadBuf = (BYTE*)malloc(IO_BUF_SIZE * sizeof(BYTE)); if(!ReadBuf) throw FI_MSG_ERROR_MEMORY; bits = FreeImage_GetScanLine(dib, height - 1); int ReadPos = IO_BUF_SIZE; if ((header.planes == 1) && ((header.bpp == 1) || (header.bpp == 8))) { BYTE skip; unsigned written; for (unsigned y = 0; y < height; y++) { written = readline(*io, handle, bits, linelength, bIsRLE, ReadBuf, &ReadPos); // skip trailing garbage at the end of the scanline for (unsigned count = written; count < linelength; count++) { if (ReadPos < IO_BUF_SIZE) { ReadPos++; } else { io->read_proc(&skip, sizeof(BYTE), 1, handle); } } bits -= pitch; } } else if ((header.planes == 4) && (header.bpp == 1)) { BYTE bit, mask, skip; unsigned index; BYTE *buffer; unsigned x, y, written; buffer = (BYTE*)malloc(width * sizeof(BYTE)); if(!buffer) throw FI_MSG_ERROR_MEMORY; for (y = 0; y < height; y++) { written = readline(*io, handle, line, linelength, bIsRLE, ReadBuf, &ReadPos); // build a nibble using the 4 planes memset(buffer, 0, width * sizeof(BYTE)); for(int plane = 0; plane < 4; plane++) { bit = (BYTE)(1 << plane); for (x = 0; x < width; x++) { index = (unsigned)((x / 8) + plane * header.bytes_per_line); mask = (BYTE)(0x80 >> (x & 0x07)); buffer[x] |= (line[index] & mask) ? bit : 0; } } // then write the DIB row for (x = 0; x < width / 2; x++) { bits[x] = (buffer[2*x] << 4) | buffer[2*x+1]; } // skip trailing garbage at the end of the scanline for (unsigned count = written; count < linelength; count++) { if (ReadPos < IO_BUF_SIZE) { ReadPos++; } else { io->read_proc(&skip, sizeof(BYTE), 1, handle); } } bits -= pitch; } free(buffer); } else if((header.planes == 3) && (header.bpp == 8)) { BYTE *pline; for (unsigned y = 0; y < height; y++) { readline(*io, handle, line, linelength, bIsRLE, ReadBuf, &ReadPos); // convert the plane stream to BGR (RRRRGGGGBBBB -> BGRBGRBGRBGR) // well, now with the FI_RGBA_x macros, on BIGENDIAN we convert to RGB pline = line; unsigned x; for (x = 0; x < width; x++) { bits[x * 3 + FI_RGBA_RED] = pline[x]; } pline += header.bytes_per_line; for (x = 0; x < width; x++) { bits[x * 3 + FI_RGBA_GREEN] = pline[x]; } pline += header.bytes_per_line; for (x = 0; x < width; x++) { bits[x * 3 + FI_RGBA_BLUE] = pline[x]; } pline += header.bytes_per_line; bits -= pitch; } } else { throw FI_MSG_ERROR_UNSUPPORTED_FORMAT; } free(line); free(ReadBuf); return dib; } catch (const char *text) {
static FIBITMAP * DLL_CALLCONV Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { png_structp png_ptr = NULL; png_infop info_ptr; png_uint_32 width, height; png_colorp png_palette = NULL; int color_type, palette_entries = 0; int bit_depth, pixel_depth; // pixel_depth = bit_depth * channels FIBITMAP *dib = NULL; RGBQUAD *palette = NULL; // pointer to dib palette png_bytepp row_pointers = NULL; int i; fi_ioStructure fio; fio.s_handle = handle; fio.s_io = io; if (handle) { BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS; try { // check to see if the file is in fact a PNG file BYTE png_check[PNG_BYTES_TO_CHECK]; io->read_proc(png_check, PNG_BYTES_TO_CHECK, 1, handle); if (png_sig_cmp(png_check, (png_size_t)0, PNG_BYTES_TO_CHECK) != 0) { return NULL; // Bad signature } // create the chunk manage structure png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, error_handler, warning_handler); if (!png_ptr) { return NULL; } // create the info structure info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); return NULL; } // init the IO png_set_read_fn(png_ptr, &fio, _ReadProc); if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_read_struct(&png_ptr, &info_ptr, NULL); return NULL; } // because we have already read the signature... png_set_sig_bytes(png_ptr, PNG_BYTES_TO_CHECK); // read the IHDR chunk png_read_info(png_ptr, info_ptr); png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL); pixel_depth = png_get_bit_depth(png_ptr, info_ptr) * png_get_channels(png_ptr, info_ptr); // get image data type (assume standard image type) FREE_IMAGE_TYPE image_type = FIT_BITMAP; if (bit_depth == 16) { if ((pixel_depth == 16) && (color_type == PNG_COLOR_TYPE_GRAY)) { image_type = FIT_UINT16; } else if ((pixel_depth == 48) && (color_type == PNG_COLOR_TYPE_RGB)) { image_type = FIT_RGB16; } else if ((pixel_depth == 64) && (color_type == PNG_COLOR_TYPE_RGB_ALPHA)) { image_type = FIT_RGBA16; } else { // tell libpng to strip 16 bit/color files down to 8 bits/color png_set_strip_16(png_ptr); bit_depth = 8; } } #ifndef FREEIMAGE_BIGENDIAN if((image_type == FIT_UINT16) || (image_type == FIT_RGB16) || (image_type == FIT_RGBA16)) { // turn on 16 bit byte swapping png_set_swap(png_ptr); } #endif // set some additional flags switch(color_type) { case PNG_COLOR_TYPE_RGB: case PNG_COLOR_TYPE_RGB_ALPHA: #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR // flip the RGB pixels to BGR (or RGBA to BGRA) if(image_type == FIT_BITMAP) { png_set_bgr(png_ptr); } #endif break; case PNG_COLOR_TYPE_PALETTE: // expand palette images to the full 8 bits from 2 bits/pixel if (pixel_depth == 2) { png_set_packing(png_ptr); pixel_depth = 8; } break; case PNG_COLOR_TYPE_GRAY: // expand grayscale images to the full 8 bits from 2 bits/pixel // but *do not* expand fully transparent palette entries to a full alpha channel if (pixel_depth == 2) { png_set_expand_gray_1_2_4_to_8(png_ptr); pixel_depth = 8; } break; case PNG_COLOR_TYPE_GRAY_ALPHA: // expand 8-bit greyscale + 8-bit alpha to 32-bit png_set_gray_to_rgb(png_ptr); #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR // flip the RGBA pixels to BGRA png_set_bgr(png_ptr); #endif pixel_depth = 32; break; default: throw FI_MSG_ERROR_UNSUPPORTED_FORMAT; } // unlike the example in the libpng documentation, we have *no* idea where // this file may have come from--so if it doesn't have a file gamma, don't // do any correction ("do no harm") if (png_get_valid(png_ptr, info_ptr, PNG_INFO_gAMA)) { double gamma = 0; double screen_gamma = 2.2; if (png_get_gAMA(png_ptr, info_ptr, &gamma) && ( flags & PNG_IGNOREGAMMA ) != PNG_IGNOREGAMMA) { png_set_gamma(png_ptr, screen_gamma, gamma); } } // all transformations have been registered; now update info_ptr data png_read_update_info(png_ptr, info_ptr); // color type may have changed, due to our transformations color_type = png_get_color_type(png_ptr,info_ptr); // create a DIB and write the bitmap header // set up the DIB palette, if needed switch (color_type) { case PNG_COLOR_TYPE_RGB: png_set_invert_alpha(png_ptr); if(image_type == FIT_BITMAP) { dib = FreeImage_AllocateHeader(header_only, width, height, 24, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); } else { dib = FreeImage_AllocateHeaderT(header_only, image_type, width, height, pixel_depth); } break; case PNG_COLOR_TYPE_RGB_ALPHA: if(image_type == FIT_BITMAP) { dib = FreeImage_AllocateHeader(header_only, width, height, 32, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); } else { dib = FreeImage_AllocateHeaderT(header_only, image_type, width, height, pixel_depth); } break; case PNG_COLOR_TYPE_PALETTE: dib = FreeImage_AllocateHeader(header_only, width, height, pixel_depth); png_get_PLTE(png_ptr,info_ptr, &png_palette, &palette_entries); palette_entries = MIN((unsigned)palette_entries, FreeImage_GetColorsUsed(dib)); palette = FreeImage_GetPalette(dib); // store the palette for (i = 0; i < palette_entries; i++) { palette[i].rgbRed = png_palette[i].red; palette[i].rgbGreen = png_palette[i].green; palette[i].rgbBlue = png_palette[i].blue; } break; case PNG_COLOR_TYPE_GRAY: dib = FreeImage_AllocateHeaderT(header_only, image_type, width, height, pixel_depth); if(pixel_depth <= 8) { palette = FreeImage_GetPalette(dib); palette_entries = 1 << pixel_depth; for (i = 0; i < palette_entries; i++) { palette[i].rgbRed = palette[i].rgbGreen = palette[i].rgbBlue = (BYTE)((i * 255) / (palette_entries - 1)); } } break; default: throw FI_MSG_ERROR_UNSUPPORTED_FORMAT; } // store the transparency table if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { // array of alpha (transparency) entries for palette png_bytep trans_alpha = NULL; // number of transparent entries int num_trans = 0; // graylevel or color sample values of the single transparent color for non-paletted images png_color_16p trans_color = NULL; png_get_tRNS(png_ptr, info_ptr, &trans_alpha, &num_trans, &trans_color); if((color_type == PNG_COLOR_TYPE_GRAY) && trans_color) { // single transparent color if (trans_color->gray < palette_entries) { BYTE table[256]; memset(table, 0xFF, palette_entries); table[trans_color->gray] = 0; FreeImage_SetTransparencyTable(dib, table, palette_entries); } } else if((color_type == PNG_COLOR_TYPE_PALETTE) && trans_alpha) { // transparency table FreeImage_SetTransparencyTable(dib, (BYTE *)trans_alpha, num_trans); } } // store the background color if (png_get_valid(png_ptr, info_ptr, PNG_INFO_bKGD)) { // Get the background color to draw transparent and alpha images over. // Note that even if the PNG file supplies a background, you are not required to // use it - you should use the (solid) application background if it has one. png_color_16p image_background = NULL; RGBQUAD rgbBkColor; if (png_get_bKGD(png_ptr, info_ptr, &image_background)) { rgbBkColor.rgbRed = (BYTE)image_background->red; rgbBkColor.rgbGreen = (BYTE)image_background->green; rgbBkColor.rgbBlue = (BYTE)image_background->blue; rgbBkColor.rgbReserved = 0; FreeImage_SetBackgroundColor(dib, &rgbBkColor); } } // get physical resolution if (png_get_valid(png_ptr, info_ptr, PNG_INFO_pHYs)) { png_uint_32 res_x, res_y; // we'll overload this var and use 0 to mean no phys data, // since if it's not in meters we can't use it anyway int res_unit_type = PNG_RESOLUTION_UNKNOWN; png_get_pHYs(png_ptr,info_ptr, &res_x, &res_y, &res_unit_type); if (res_unit_type == PNG_RESOLUTION_METER) { FreeImage_SetDotsPerMeterX(dib, res_x); FreeImage_SetDotsPerMeterY(dib, res_y); } } // get possible ICC profile if (png_get_valid(png_ptr, info_ptr, PNG_INFO_iCCP)) { png_charp profile_name = NULL; png_bytep profile_data = NULL; png_uint_32 profile_length = 0; int compression_type; png_get_iCCP(png_ptr, info_ptr, &profile_name, &compression_type, &profile_data, &profile_length); // copy ICC profile data (must be done after FreeImage_AllocateHeader) FreeImage_CreateICCProfile(dib, profile_data, profile_length); } // --- header only mode => clean-up and return if (header_only) { // get possible metadata (it can be located both before and after the image data) ReadMetadata(png_ptr, info_ptr, dib); if (png_ptr) { // clean up after the read, and free any memory allocated - REQUIRED png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); } return dib; } // set the individual row_pointers to point at the correct offsets row_pointers = (png_bytepp)malloc(height * sizeof(png_bytep)); if (!row_pointers) { png_destroy_read_struct(&png_ptr, &info_ptr, NULL); FreeImage_Unload(dib); return NULL; } // read in the bitmap bits via the pointer table // allow loading of PNG with minor errors (such as images with several IDAT chunks) for (png_uint_32 k = 0; k < height; k++) { row_pointers[height - 1 - k] = FreeImage_GetScanLine(dib, k); } png_set_benign_errors(png_ptr, 1); png_read_image(png_ptr, row_pointers); // check if the bitmap contains transparency, if so enable it in the header if (FreeImage_GetBPP(dib) == 32) { if (FreeImage_GetColorType(dib) == FIC_RGBALPHA) { FreeImage_SetTransparent(dib, TRUE); } else { FreeImage_SetTransparent(dib, FALSE); } } // cleanup if (row_pointers) { free(row_pointers); row_pointers = NULL; } // read the rest of the file, getting any additional chunks in info_ptr png_read_end(png_ptr, info_ptr); // get possible metadata (it can be located both before and after the image data) ReadMetadata(png_ptr, info_ptr, dib); if (png_ptr) { // clean up after the read, and free any memory allocated - REQUIRED png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); } return dib; } catch (const char *text) { if (png_ptr) { png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); } if (row_pointers) { free(row_pointers); } if (dib) { FreeImage_Unload(dib); } FreeImage_OutputMessageProc(s_format_id, text); return NULL; } } return NULL; }
/** Decode a WebP image and returns a FIBITMAP image @param webp_image Raw WebP image @param flags FreeImage load flags @return Returns a dib if successfull, returns NULL otherwise */ static FIBITMAP * DecodeImage(WebPData *webp_image, int flags) { FIBITMAP *dib = NULL; const uint8_t* data = webp_image->bytes; // raw image data const size_t data_size = webp_image->size; // raw image size VP8StatusCode webp_status = VP8_STATUS_OK; BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS; // Main object storing the configuration for advanced decoding WebPDecoderConfig decoder_config; // Output buffer WebPDecBuffer* const output_buffer = &decoder_config.output; // Features gathered from the bitstream WebPBitstreamFeatures* const bitstream = &decoder_config.input; try { // Initialize the configuration as empty // This function must always be called first, unless WebPGetFeatures() is to be called if(!WebPInitDecoderConfig(&decoder_config)) { throw "Library version mismatch"; } // Retrieve features from the bitstream // The bitstream structure is filled with information gathered from the bitstream webp_status = WebPGetFeatures(data, data_size, bitstream); if(webp_status != VP8_STATUS_OK) { throw FI_MSG_ERROR_PARSING; } // Allocate output dib unsigned bpp = bitstream->has_alpha ? 32 : 24; unsigned width = (unsigned)bitstream->width; unsigned height = (unsigned)bitstream->height; dib = FreeImage_AllocateHeader(header_only, width, height, bpp, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); if(!dib) { throw FI_MSG_ERROR_DIB_MEMORY; } if(header_only) { WebPFreeDecBuffer(output_buffer); return dib; } // --- Set decoding options --- // use multi-threaded decoding decoder_config.options.use_threads = 1; // set output color space output_buffer->colorspace = bitstream->has_alpha ? MODE_BGRA : MODE_BGR; // --- // decode the input stream, taking 'config' into account. webp_status = WebPDecode(data, data_size, &decoder_config); if(webp_status != VP8_STATUS_OK) { throw FI_MSG_ERROR_PARSING; } // fill the dib with the decoded data const BYTE *src_bitmap = output_buffer->u.RGBA.rgba; const unsigned src_pitch = (unsigned)output_buffer->u.RGBA.stride; switch(bpp) { case 24: for(unsigned y = 0; y < height; y++) { const BYTE *src_bits = src_bitmap + y * src_pitch; BYTE *dst_bits = (BYTE*)FreeImage_GetScanLine(dib, height-1-y); for(unsigned x = 0; x < width; x++) { dst_bits[FI_RGBA_BLUE] = src_bits[0]; // B dst_bits[FI_RGBA_GREEN] = src_bits[1]; // G dst_bits[FI_RGBA_RED] = src_bits[2]; // R src_bits += 3; dst_bits += 3; } } break; case 32: for(unsigned y = 0; y < height; y++) { const BYTE *src_bits = src_bitmap + y * src_pitch; BYTE *dst_bits = (BYTE*)FreeImage_GetScanLine(dib, height-1-y); for(unsigned x = 0; x < width; x++) { dst_bits[FI_RGBA_BLUE] = src_bits[0]; // B dst_bits[FI_RGBA_GREEN] = src_bits[1]; // G dst_bits[FI_RGBA_RED] = src_bits[2]; // R dst_bits[FI_RGBA_ALPHA] = src_bits[3]; // A src_bits += 4; dst_bits += 4; } } break; } // Free the decoder WebPFreeDecBuffer(output_buffer); return dib; } catch (const char *text) { if(dib) { FreeImage_Unload(dib); } WebPFreeDecBuffer(output_buffer); if(NULL != text) { FreeImage_OutputMessageProc(s_format_id, text); } return NULL; } }
static FIBITMAP * DLL_CALLCONV Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { SUNHEADER header; // Sun file header WORD linelength; // Length of raster line in bytes WORD fill; // Number of fill bytes per raster line BOOL rle; // TRUE if RLE file BOOL isRGB; // TRUE if file type is RT_FORMAT_RGB BYTE fillchar; FIBITMAP *dib = NULL; BYTE *bits; // Pointer to dib data WORD x, y; if(!handle) { return NULL; } BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS; try { // Read SUN raster header io->read_proc(&header, sizeof(SUNHEADER), 1, handle); #ifndef FREEIMAGE_BIGENDIAN // SUN rasterfiles are big endian only SwapLong(&header.magic); SwapLong(&header.width); SwapLong(&header.height); SwapLong(&header.depth); SwapLong(&header.length); SwapLong(&header.type); SwapLong(&header.maptype); SwapLong(&header.maplength); #endif // Verify SUN identifier if (header.magic != RAS_MAGIC) { throw FI_MSG_ERROR_MAGIC_NUMBER; } // Allocate a new DIB switch(header.depth) { case 1: case 8: dib = FreeImage_AllocateHeader(header_only, header.width, header.height, header.depth); break; case 24: dib = FreeImage_AllocateHeader(header_only, header.width, header.height, header.depth, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); break; case 32: dib = FreeImage_AllocateHeader(header_only, header.width, header.height, header.depth, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); break; } if (dib == NULL) { throw FI_MSG_ERROR_DIB_MEMORY; } // Check the file format rle = FALSE; isRGB = FALSE; switch(header.type) { case RT_OLD: case RT_STANDARD: case RT_FORMAT_TIFF: // I don't even know what these format are... case RT_FORMAT_IFF: //The TIFF and IFF format types indicate that the raster //file was originally converted from either of these file formats. //so lets at least try to process them as RT_STANDARD break; case RT_BYTE_ENCODED: rle = TRUE; break; case RT_FORMAT_RGB: isRGB = TRUE; break; default: throw FI_MSG_ERROR_UNSUPPORTED_FORMAT; } // set up the colormap if needed switch(header.maptype) { case RMT_NONE : { if (header.depth < 24) { // Create linear color ramp RGBQUAD *pal = FreeImage_GetPalette(dib); int numcolors = 1 << header.depth; for (int i = 0; i < numcolors; i++) { pal[i].rgbRed = (BYTE)((255 * i) / (numcolors - 1)); pal[i].rgbGreen = (BYTE)((255 * i) / (numcolors - 1)); pal[i].rgbBlue = (BYTE)((255 * i) / (numcolors - 1)); } } break; } case RMT_EQUAL_RGB: { BYTE *r, *g, *b; // Read SUN raster colormap int numcolors = 1 << header.depth; if((DWORD)(3 * numcolors) > header.maplength) { // some RAS may have less colors than the full palette numcolors = header.maplength / 3; } else { throw "Invalid palette"; } r = (BYTE*)malloc(3 * numcolors * sizeof(BYTE)); g = r + numcolors; b = g + numcolors; RGBQUAD *pal = FreeImage_GetPalette(dib); io->read_proc(r, 3 * numcolors, 1, handle); for (int i = 0; i < numcolors; i++) { pal[i].rgbRed = r[i]; pal[i].rgbGreen = g[i]; pal[i].rgbBlue = b[i]; } free(r); break; } case RMT_RAW: { BYTE *colormap; // Read (skip) SUN raster colormap. colormap = (BYTE *)malloc(header.maplength * sizeof(BYTE)); io->read_proc(colormap, header.maplength, 1, handle); free(colormap); break; } } if(header_only) { // header only mode return dib; } // Calculate the line + pitch // Each row is multiple of 16 bits (2 bytes). if (header.depth == 1) { linelength = (WORD)((header.width / 8) + (header.width % 8 ? 1 : 0)); } else { linelength = (WORD)header.width; } fill = (linelength % 2) ? 1 : 0; unsigned pitch = FreeImage_GetPitch(dib); // Read the image data switch(header.depth) { case 1: case 8: { bits = FreeImage_GetBits(dib) + (header.height - 1) * pitch; for (y = 0; y < header.height; y++) { ReadData(io, handle, bits, linelength, rle); bits -= pitch; if (fill) { ReadData(io, handle, &fillchar, fill, rle); } } break; } case 24: { BYTE *buf, *bp; buf = (BYTE*)malloc(header.width * 3); for (y = 0; y < header.height; y++) { bits = FreeImage_GetBits(dib) + (header.height - 1 - y) * pitch; ReadData(io, handle, buf, header.width * 3, rle); bp = buf; if (isRGB) { for (x = 0; x < header.width; x++) { bits[FI_RGBA_RED] = *(bp++); // red bits[FI_RGBA_GREEN] = *(bp++); // green bits[FI_RGBA_BLUE] = *(bp++); // blue bits += 3; } } else { for (x = 0; x < header.width; x++) { bits[FI_RGBA_RED] = *(bp + 2); // red bits[FI_RGBA_GREEN] = *(bp + 1);// green bits[FI_RGBA_BLUE] = *bp; // blue bits += 3; bp += 3; } } if (fill) { ReadData(io, handle, &fillchar, fill, rle); } } free(buf); break; } case 32: { BYTE *buf, *bp; buf = (BYTE*)malloc(header.width * 4); for (y = 0; y < header.height; y++) { bits = FreeImage_GetBits(dib) + (header.height - 1 - y) * pitch; ReadData(io, handle, buf, header.width * 4, rle); bp = buf; if (isRGB) { for (x = 0; x < header.width; x++) { bits[FI_RGBA_ALPHA] = *(bp++); // alpha bits[FI_RGBA_RED] = *(bp++); // red bits[FI_RGBA_GREEN] = *(bp++); // green bits[FI_RGBA_BLUE] = *(bp++); // blue bits += 4; } } else { for (x = 0; x < header.width; x++) { bits[FI_RGBA_RED] = *(bp + 3); // red bits[FI_RGBA_GREEN] = *(bp + 2); // green bits[FI_RGBA_BLUE] = *(bp + 1); // blue bits[FI_RGBA_ALPHA] = *bp; // alpha bits += 4; bp += 4; } } if (fill) { ReadData(io, handle, &fillchar, fill, rle); } } free(buf); break; } } return dib; } catch (const char *text) { if(dib) { FreeImage_Unload(dib); } FreeImage_OutputMessageProc(s_format_id, text); } return NULL; }
static FIBITMAP * DLL_CALLCONV Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { FIBITMAP *dib = NULL; unsigned width; unsigned height; const unsigned bpp = 24; int scan_line_add = 1; int start_scan_line = 0; BYTE *y1 = NULL, *y2 = NULL, *cbcr = NULL; BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS; // to make absolute seeks possible we store the current position in the file long offset_in_file = io->tell_proc(handle); long seek = 0; // decide which bitmap in the cabinet to load switch (flags) { case PCD_BASEDIV4 : seek = 0x2000; width = 192; height = 128; break; case PCD_BASEDIV16 : seek = 0xB800; width = 384; height = 256; break; default : seek = 0x30000; width = 768; height = 512; break; } try { // allocate the dib and write out the header dib = FreeImage_AllocateHeader(header_only, width, height, bpp, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); if(!dib) throw FI_MSG_ERROR_DIB_MEMORY; if(header_only) { return dib; } // check if the PCD is bottom-up if (VerticalOrientation(io, handle)) { scan_line_add = -1; start_scan_line = height - 1; } // temporary stuff to load PCD BYTE *y1 = (BYTE*)malloc(width * sizeof(BYTE)); BYTE *y2 = (BYTE*)malloc(width * sizeof(BYTE)); BYTE *cbcr = (BYTE*)malloc(width * sizeof(BYTE)); if(!y1 || !y2 || !cbcr) throw FI_MSG_ERROR_MEMORY; BYTE *yl[] = { y1, y2 }; // seek to the part where the bitmap data begins io->seek_proc(handle, offset_in_file, SEEK_SET); io->seek_proc(handle, seek, SEEK_CUR); // read the data for (unsigned y = 0; y < height / 2; y++) { io->read_proc(y1, width, 1, handle); io->read_proc(y2, width, 1, handle); io->read_proc(cbcr, width, 1, handle); for (int i = 0; i < 2; i++) { BYTE *bits = FreeImage_GetScanLine(dib, start_scan_line); for (unsigned x = 0; x < width; x++) { int r, g, b; YUV2RGB(yl[i][x], cbcr[x / 2], cbcr[(width / 2) + (x / 2)], r, g, b); bits[FI_RGBA_BLUE] = (BYTE)b; bits[FI_RGBA_GREEN] = (BYTE)g; bits[FI_RGBA_RED] = (BYTE)r; bits += 3; } start_scan_line += scan_line_add; } } free(cbcr); free(y2); free(y1); return dib; } catch(const char *text) { if(dib) FreeImage_Unload(dib); if(cbcr) free(cbcr); if(y2) free(y2); if(y1) free(y1); FreeImage_OutputMessageProc(s_format_id, text); return NULL; } }
static FIBITMAP * DLL_CALLCONV Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { char msg[256]; FIBITMAP *dib = NULL; if (!handle) return NULL; try { char *str; BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS; //find the starting brace if( !FindChar(io, handle,'{') ) throw "Could not find starting brace"; //read info string str = ReadString(io, handle); if(!str) throw "Error reading info string"; int width, height, colors, cpp; if( sscanf(str, "%d %d %d %d", &width, &height, &colors, &cpp) != 4 ) { free(str); throw "Improperly formed info string"; } free(str); if (colors > 256) { dib = FreeImage_AllocateHeader(header_only, width, height, 24, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); } else { dib = FreeImage_AllocateHeader(header_only, width, height, 8); } //build a map of color chars to rgb values std::map<std::string,FILE_RGBA> rawpal; //will store index in Alpha if 8bpp for(int i = 0; i < colors; i++ ) { FILE_RGBA rgba; str = ReadString(io, handle); if(!str) throw "Error reading color strings"; std::string chrs(str,cpp); //create a string for the color chars using the first cpp chars char *keys = str + cpp; //the color keys for these chars start after the first cpp chars //translate all the tabs to spaces char *tmp = keys; while( strchr(tmp,'\t') ) { tmp = strchr(tmp,'\t'); *tmp++ = ' '; } //prefer the color visual if( strstr(keys," c ") ) { char *clr = strstr(keys," c ") + 3; while( *clr == ' ' ) clr++; //find the start of the hex rgb value if( *clr == '#' ) { int red = 0, green = 0, blue = 0, n; clr++; //end string at first space, if any found if( strchr(clr,' ') ) *(strchr(clr,' ')) = '\0'; //parse hex color, it can be #rgb #rrggbb #rrrgggbbb or #rrrrggggbbbb switch( strlen(clr) ) { case 3: n = sscanf(clr,"%01x%01x%01x",&red,&green,&blue); red |= (red << 4); green |= (green << 4); blue |= (blue << 4); break; case 6: n = sscanf(clr,"%02x%02x%02x",&red,&green,&blue); break; case 9: n = sscanf(clr,"%03x%03x%03x",&red,&green,&blue); red >>= 4; green >>= 4; blue >>= 4; break; case 12: n = sscanf(clr,"%04x%04x%04x",&red,&green,&blue); red >>= 8; green >>= 8; blue >>= 8; break; default: n = 0; break; } if( n != 3 ) { free(str); throw "Improperly formed hex color value"; } rgba.r = (BYTE)red; rgba.g = (BYTE)green; rgba.b = (BYTE)blue; } else if( !strncmp(clr,"None",4) || !strncmp(clr,"none",4) ) { rgba.r = rgba.g = rgba.b = 0xFF; } else { char *tmp = clr; //scan forward for each space, if its " x " or " xx " end the string there //this means its probably some other visual data beyond that point and not //part of the color name. How many named color end with a 1 or 2 character //word? Probably none in our list at least. while( (tmp = strchr(tmp,' ')) != NULL ) { if( tmp[1] != ' ' ) { if( (tmp[2] == ' ') || (tmp[2] != ' ' && tmp[3] == ' ') ) { tmp[0] = '\0'; break; } } tmp++; } //remove any trailing spaces tmp = clr+strlen(clr)-1; while( *tmp == ' ' ) { *tmp = '\0'; tmp--; } if (!FreeImage_LookupX11Color(clr, &rgba.r, &rgba.g, &rgba.b)) { sprintf(msg, "Unknown color name '%s'", str); free(str); throw msg; } } } else {
/** Convert a OpenJPEG image to a FIBITMAP @param format_id Plugin ID @param image OpenJPEG image @param header_only If TRUE, allocate a 'header only' FIBITMAP, otherwise allocate a full FIBITMAP @return Returns the converted image if successful, returns NULL otherwise */ FIBITMAP* J2KImageToFIBITMAP(int format_id, const opj_image_t *image, BOOL header_only) { FIBITMAP *dib = NULL; try { // compute image width and height //int w = int_ceildiv(image->x1 - image->x0, image->comps[0].dx); int wr = image->comps[0].w; int wrr = int_ceildivpow2(image->comps[0].w, image->comps[0].factor); //int h = int_ceildiv(image->y1 - image->y0, image->comps[0].dy); //int hr = image->comps[0].h; int hrr = int_ceildivpow2(image->comps[0].h, image->comps[0].factor); // check the number of components int numcomps = image->numcomps; BOOL bIsValid = TRUE; for(int c = 0; c < numcomps - 1; c++) { if( (image->comps[c].dx == image->comps[c+1].dx) && (image->comps[c].dy == image->comps[c+1].dy) && (image->comps[c].prec == image->comps[c+1].prec) ) { continue; } else { bIsValid = FALSE; break; } } bIsValid &= ((numcomps == 1) || (numcomps == 3) || (numcomps == 4)); if(!bIsValid) { if(numcomps) { FreeImage_OutputMessageProc(format_id, "Warning: image contains %d greyscale components. Only the first will be loaded.\n", numcomps); numcomps = 1; } else { // unknown type throw FI_MSG_ERROR_UNSUPPORTED_FORMAT; } } // create a new DIB if(image->comps[0].prec <= 8) { switch(numcomps) { case 1: dib = FreeImage_AllocateHeader(header_only, wrr, hrr, 8); break; case 3: dib = FreeImage_AllocateHeader(header_only, wrr, hrr, 24, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); break; case 4: dib = FreeImage_AllocateHeader(header_only, wrr, hrr, 32, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); break; } } else if(image->comps[0].prec <= 16) { switch(numcomps) { case 1: dib = FreeImage_AllocateHeaderT(header_only, FIT_UINT16, wrr, hrr); break; case 3: dib = FreeImage_AllocateHeaderT(header_only, FIT_RGB16, wrr, hrr); break; case 4: dib = FreeImage_AllocateHeaderT(header_only, FIT_RGBA16, wrr, hrr); break; } } else { throw FI_MSG_ERROR_UNSUPPORTED_FORMAT; } if(!dib) { throw FI_MSG_ERROR_DIB_MEMORY; } // "header only" FIBITMAP ? if(header_only) { return dib; } if(image->comps[0].prec <= 8) { if(numcomps == 1) { // 8-bit greyscale // ---------------------------------------------------------- // build a greyscale palette RGBQUAD *pal = FreeImage_GetPalette(dib); for (int i = 0; i < 256; i++) { pal[i].rgbRed = (BYTE)i; pal[i].rgbGreen = (BYTE)i; pal[i].rgbBlue = (BYTE)i; } // load pixel data unsigned pixel_count = 0; for(int y = 0; y < hrr; y++) { BYTE *bits = FreeImage_GetScanLine(dib, hrr - 1 - y); for(int x = 0; x < wrr; x++) { const unsigned pixel_pos = pixel_count / wrr * wr + pixel_count % wrr; int index = image->comps[0].data[pixel_pos]; index += (image->comps[0].sgnd ? 1 << (image->comps[0].prec - 1) : 0); bits[x] = (BYTE)index; pixel_count++; } } } else if(numcomps == 3) { // 24-bit RGB // ---------------------------------------------------------- // load pixel data unsigned pixel_count = 0; for(int y = 0; y < hrr; y++) { BYTE *bits = FreeImage_GetScanLine(dib, hrr - 1 - y); for(int x = 0; x < wrr; x++) { const unsigned pixel_pos = pixel_count / wrr * wr + pixel_count % wrr; int r = image->comps[0].data[pixel_pos]; r += (image->comps[0].sgnd ? 1 << (image->comps[0].prec - 1) : 0); int g = image->comps[1].data[pixel_pos]; g += (image->comps[1].sgnd ? 1 << (image->comps[1].prec - 1) : 0); int b = image->comps[2].data[pixel_pos]; b += (image->comps[2].sgnd ? 1 << (image->comps[2].prec - 1) : 0); bits[FI_RGBA_RED] = (BYTE)r; bits[FI_RGBA_GREEN] = (BYTE)g; bits[FI_RGBA_BLUE] = (BYTE)b; bits += 3; pixel_count++; } } } else if(numcomps == 4) { // 32-bit RGBA // ---------------------------------------------------------- // load pixel data unsigned pixel_count = 0; for(int y = 0; y < hrr; y++) { BYTE *bits = FreeImage_GetScanLine(dib, hrr - 1 - y); for(int x = 0; x < wrr; x++) { const unsigned pixel_pos = pixel_count / wrr * wr + pixel_count % wrr; int r = image->comps[0].data[pixel_pos]; r += (image->comps[0].sgnd ? 1 << (image->comps[0].prec - 1) : 0); int g = image->comps[1].data[pixel_pos]; g += (image->comps[1].sgnd ? 1 << (image->comps[1].prec - 1) : 0); int b = image->comps[2].data[pixel_pos]; b += (image->comps[2].sgnd ? 1 << (image->comps[2].prec - 1) : 0); int a = image->comps[3].data[pixel_pos]; a += (image->comps[3].sgnd ? 1 << (image->comps[3].prec - 1) : 0); bits[FI_RGBA_RED] = (BYTE)r; bits[FI_RGBA_GREEN] = (BYTE)g; bits[FI_RGBA_BLUE] = (BYTE)b; bits[FI_RGBA_ALPHA] = (BYTE)a; bits += 4; pixel_count++; } } } } else if(image->comps[0].prec <= 16) { if(numcomps == 1) { // 16-bit greyscale // ---------------------------------------------------------- // load pixel data unsigned pixel_count = 0; for(int y = 0; y < hrr; y++) { WORD *bits = (WORD*)FreeImage_GetScanLine(dib, hrr - 1 - y); for(int x = 0; x < wrr; x++) { const unsigned pixel_pos = pixel_count / wrr * wr + pixel_count % wrr; int index = image->comps[0].data[pixel_pos]; index += (image->comps[0].sgnd ? 1 << (image->comps[0].prec - 1) : 0); bits[x] = (WORD)index; pixel_count++; } } } else if(numcomps == 3) { // 48-bit RGB // ---------------------------------------------------------- // load pixel data unsigned pixel_count = 0; for(int y = 0; y < hrr; y++) { FIRGB16 *bits = (FIRGB16*)FreeImage_GetScanLine(dib, hrr - 1 - y); for(int x = 0; x < wrr; x++) { const unsigned pixel_pos = pixel_count / wrr * wr + pixel_count % wrr; int r = image->comps[0].data[pixel_pos]; r += (image->comps[0].sgnd ? 1 << (image->comps[0].prec - 1) : 0); int g = image->comps[1].data[pixel_pos]; g += (image->comps[1].sgnd ? 1 << (image->comps[1].prec - 1) : 0); int b = image->comps[2].data[pixel_pos]; b += (image->comps[2].sgnd ? 1 << (image->comps[2].prec - 1) : 0); bits[x].red = (WORD)r; bits[x].green = (WORD)g; bits[x].blue = (WORD)b; pixel_count++; } } } else if(numcomps == 4) { // 64-bit RGBA // ---------------------------------------------------------- // load pixel data unsigned pixel_count = 0; for(int y = 0; y < hrr; y++) { FIRGBA16 *bits = (FIRGBA16*)FreeImage_GetScanLine(dib, hrr - 1 - y); for(int x = 0; x < wrr; x++) { const unsigned pixel_pos = pixel_count / wrr * wr + pixel_count % wrr; int r = image->comps[0].data[pixel_pos]; r += (image->comps[0].sgnd ? 1 << (image->comps[0].prec - 1) : 0); int g = image->comps[1].data[pixel_pos]; g += (image->comps[1].sgnd ? 1 << (image->comps[1].prec - 1) : 0); int b = image->comps[2].data[pixel_pos]; b += (image->comps[2].sgnd ? 1 << (image->comps[2].prec - 1) : 0); int a = image->comps[3].data[pixel_pos]; a += (image->comps[3].sgnd ? 1 << (image->comps[3].prec - 1) : 0); bits[x].red = (WORD)r; bits[x].green = (WORD)g; bits[x].blue = (WORD)b; bits[x].alpha = (WORD)a; pixel_count++; } } } } return dib; } catch(const char *text) { if(dib) FreeImage_Unload(dib); FreeImage_OutputMessageProc(format_id, text); return NULL; } }