bool MCImageDecodePNG(IO_handle p_stream, MCImageBitmap *&r_bitmap) { bool t_success = true; MCImageBitmap *t_bitmap = nil; png_structp t_png = nil; png_infop t_info = nil; png_infop t_end_info = nil; t_success = nil != (t_png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL)); if (t_success) t_success = nil != (t_info = png_create_info_struct(t_png)); if (t_success) t_success = nil != (t_end_info = png_create_info_struct(t_png)); if (setjmp(png_jmpbuf(t_png))) { t_success = false; } if (t_success) { png_set_read_fn(t_png, p_stream, stream_read); png_read_info(t_png, t_info); } png_uint_32 t_width, t_height; int t_bit_depth, t_color_type; int t_interlace_method, t_compression_method, t_filter_method; int t_interlace_passes; if (t_success) { png_get_IHDR(t_png, t_info, &t_width, &t_height, &t_bit_depth, &t_color_type, &t_interlace_method, &t_compression_method, &t_filter_method); } if (t_success) t_success = MCImageBitmapCreate(t_width, t_height, t_bitmap); if (t_success) { bool t_need_alpha = false; t_interlace_passes = png_set_interlace_handling(t_png); if (t_color_type == PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb(t_png); if (t_color_type == PNG_COLOR_TYPE_GRAY || t_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(t_png); if (png_get_valid(t_png, t_info, PNG_INFO_tRNS)) { png_set_tRNS_to_alpha(t_png); t_need_alpha = true; /* OVERHAUL - REVISIT - assume image has transparent pixels if tRNS is present */ t_bitmap->has_transparency = true; } if (t_color_type & PNG_COLOR_MASK_ALPHA) { t_need_alpha = true; /* OVERHAUL - REVISIT - assume image has alpha if color type allows it */ t_bitmap->has_alpha = t_bitmap->has_transparency = true; } else if (!t_need_alpha) png_set_add_alpha(t_png, 0xFF, MCswapbytes ? PNG_FILLER_AFTER : PNG_FILLER_BEFORE); if (t_bit_depth == 16) png_set_strip_16(t_png); if (MCswapbytes) png_set_bgr(t_png); else png_set_swap_alpha(t_png); } // MW-2009-12-10: Support for color profiles MCColorTransformRef t_color_xform; t_color_xform = nil; // Try to get an embedded ICC profile... if (t_success && t_color_xform == nil && png_get_valid(t_png, t_info, PNG_INFO_iCCP)) { png_charp t_ccp_name; png_bytep t_ccp_profile; int t_ccp_compression_type; png_uint_32 t_ccp_profile_length; png_get_iCCP(t_png, t_info, &t_ccp_name, &t_ccp_compression_type, &t_ccp_profile, &t_ccp_profile_length); MCColorSpaceInfo t_csinfo; t_csinfo . type = kMCColorSpaceEmbedded; t_csinfo . embedded . data = t_ccp_profile; t_csinfo . embedded . data_size = t_ccp_profile_length; t_color_xform = MCscreen -> createcolortransform(t_csinfo); } // Next try an sRGB style profile... if (t_success && t_color_xform == nil && png_get_valid(t_png, t_info, PNG_INFO_sRGB)) { int t_intent; png_get_sRGB(t_png, t_info, &t_intent); MCColorSpaceInfo t_csinfo; t_csinfo . type = kMCColorSpaceStandardRGB; t_csinfo . standard . intent = (MCColorSpaceIntent)t_intent; t_color_xform = MCscreen -> createcolortransform(t_csinfo); } // Finally try for cHRM + gAMA... if (t_success && t_color_xform == nil && png_get_valid(t_png, t_info, PNG_INFO_cHRM) && png_get_valid(t_png, t_info, PNG_INFO_gAMA)) { MCColorSpaceInfo t_csinfo; t_csinfo . type = kMCColorSpaceCalibratedRGB; png_get_cHRM(t_png, t_info, &t_csinfo . calibrated . white_x, &t_csinfo . calibrated . white_y, &t_csinfo . calibrated . red_x, &t_csinfo . calibrated . red_y, &t_csinfo . calibrated . green_x, &t_csinfo . calibrated . green_y, &t_csinfo . calibrated . blue_x, &t_csinfo . calibrated . blue_y); png_get_gAMA(t_png, t_info, &t_csinfo . calibrated . gamma); t_color_xform = MCscreen -> createcolortransform(t_csinfo); } // Could not create any kind, so fallback to gamma transform. if (t_success && t_color_xform == nil) { double image_gamma; if (png_get_gAMA(t_png, t_info, &image_gamma)) png_set_gamma(t_png, MCgamma, image_gamma); else png_set_gamma(t_png, MCgamma, 0.45); } if (t_success) { for (uindex_t t_pass = 0; t_pass < t_interlace_passes; t_pass++) { png_bytep t_data_ptr = (png_bytep)t_bitmap->data; for (uindex_t i = 0; i < t_height; i++) { png_read_row(t_png, t_data_ptr, nil); t_data_ptr += t_bitmap->stride; } } } if (t_success) png_read_end(t_png, t_end_info); if (t_png != nil) png_destroy_read_struct(&t_png, &t_info, &t_end_info); // transform colours using extracted colour profile if (t_success && t_color_xform != nil) MCImageBitmapApplyColorTransform(t_bitmap, t_color_xform); if (t_color_xform != nil) MCscreen -> destroycolortransform(t_color_xform); if (t_success) r_bitmap = t_bitmap; else { if (t_bitmap != nil) MCImageFreeBitmap(t_bitmap); } return t_success; }
bool MCGIFImageLoader::LoadFrames(MCBitmapFrame *&r_frames, uint32_t &r_count) { bool t_success; t_success = true; MCImageBitmap *t_canvas; t_canvas = nil; // restoration info MCImageBitmap *t_restore_image = nil; int t_disposal_mode = DISPOSAL_UNSPECIFIED; MCRectangle t_disposal_region = kMCEmptyRectangle; // The list of frames. MCBitmapFrame *t_frames = nil; t_success = GIF_OK == DGifSlurp(m_gif); // Fetch the width and height of the virtual canvas. int32_t t_width, t_height; if (t_success) { t_width = m_gif -> SWidth; t_height = m_gif -> SHeight; // create the canvas image t_success = MCImageBitmapCreate(t_width, t_height, t_canvas); } // The current frame count. The number of frames is the same as the // number of images in the GIF. uint32_t t_frame_count; t_frame_count = 0; // If true, the new image will be merged with the old - otherwise the mask // replaces it. bool t_overlay; t_overlay = false; // Loop through all the images, making frames as we go. for(uindex_t i = 0; t_success && i < m_gif -> ImageCount; i++) { // Process the disposal. switch (t_disposal_mode) { case DISPOSE_BACKGROUND: gif_fill_image_region(t_canvas, t_disposal_region, 0); break; case DISPOSE_PREVIOUS: if (t_restore_image != nil) gif_paste_image(t_canvas, t_restore_image, t_disposal_region.x, t_disposal_region.y); break; case DISPOSE_DO_NOT: t_overlay = true; break; default: t_overlay = false; break; } // Fetch the image information. GraphicsControlBlock t_image_gcb; MCRectangle t_image_region; ColorMapObject *t_image_colors = nil; int32_t t_image_transparency; int32_t t_image_delay; int32_t t_image_disposal; GifByteType *t_image_raster; // First the information from the image description. t_image_region.x = m_gif -> SavedImages[i] . ImageDesc . Left; t_image_region.y = m_gif -> SavedImages[i] . ImageDesc . Top; t_image_region.width = m_gif -> SavedImages[i] . ImageDesc . Width; t_image_region.height = m_gif -> SavedImages[i] . ImageDesc . Height; t_image_colors = m_gif -> SavedImages[i] . ImageDesc . ColorMap; t_image_raster = m_gif -> SavedImages[i] . RasterBits; if (t_image_colors == nil) t_image_colors = m_gif -> SColorMap; // Then the information from the GCB. if (GIF_OK == DGifSavedExtensionToGCB(m_gif, i, &t_image_gcb)) { t_image_transparency = t_image_gcb . TransparentColor; t_image_delay = t_image_gcb . DelayTime; t_image_disposal = t_image_gcb . DisposalMode; } else { t_image_transparency = -1; t_image_delay = 0; t_image_disposal = DISPOSAL_UNSPECIFIED; } // If disposal is 'previous' then cache the portion of the canvas we are // about to affect. if (t_image_disposal == DISPOSE_PREVIOUS) { if (t_restore_image != nil) { if (t_disposal_mode != DISPOSE_PREVIOUS || !MCU_equal_rect(t_disposal_region, t_image_region)) { MCImageFreeBitmap(t_restore_image); t_restore_image = nil; } } if (t_restore_image == nil) t_success = MCImageCopyBitmapRegion(t_canvas, t_image_region, t_restore_image); } if (t_success) { // Render the image into the canvas. gif_draw_image_into_canvas(t_canvas, t_image_raster, t_image_region.x, t_image_region.y, t_image_region.width, t_image_region.height, t_image_colors, t_image_transparency, t_overlay); // Generate our frame. t_success = MCMemoryResizeArray(t_frame_count + 1, t_frames, t_frame_count); } MCImageBitmap *t_frame_bitmap = nil; if (t_success) t_success = MCImageCopyBitmap(t_canvas, t_frame_bitmap); if (t_success) { MCImageBitmapCheckTransparency(t_frame_bitmap); t_frames[t_frame_count - 1].image = t_frame_bitmap; t_frames[t_frame_count - 1].duration = t_image_delay * 10; // convert 1/100 seconds to milliseconds t_frames[t_frame_count - 1].x_scale = t_frames[t_frame_count - 1].y_scale = 1.0; } t_disposal_region = t_image_region; t_disposal_mode = t_image_disposal; } MCImageFreeBitmap(t_canvas); MCImageFreeBitmap(t_restore_image); if (t_success) { r_frames = t_frames; r_count = t_frame_count; } else MCImageFreeFrames(t_frames, t_frame_count); return t_success; }