This gives a much clearer overview of the licensing. It also shows there are some problems: - Some files are under GPLv3 only - Some files have no known license at all.
		
			
				
	
	
		
			2284 lines
		
	
	
		
			70 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			2284 lines
		
	
	
		
			70 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* vim:expandtab:ts=2 sw=2:
 | 
						|
*/
 | 
						|
/*  Grafx2 - The Ultimate 256-color bitmap paint program
 | 
						|
 | 
						|
	Copyright owned by various GrafX2 authors, see COPYRIGHT.txt for details.
 | 
						|
 | 
						|
    Grafx2 is free software; you can redistribute it and/or
 | 
						|
    modify it under the terms of the GNU General Public License
 | 
						|
    as published by the Free Software Foundation; version 2
 | 
						|
    of the License.
 | 
						|
 | 
						|
    Grafx2 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 General Public License for more details.
 | 
						|
 | 
						|
    You should have received a copy of the GNU General Public License
 | 
						|
    along with Grafx2; if not, see <http://www.gnu.org/licenses/>
 | 
						|
*/
 | 
						|
 | 
						|
#include <fcntl.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <string.h>
 | 
						|
#ifndef _MSC_VER
 | 
						|
#include <unistd.h>
 | 
						|
#endif
 | 
						|
#if defined(WIN32)
 | 
						|
#include <windows.h>
 | 
						|
#if defined(_MSC_VER)
 | 
						|
#define strdup _strdup
 | 
						|
#if _MSC_VER < 1900
 | 
						|
#define snprintf _snprintf
 | 
						|
#endif
 | 
						|
#endif
 | 
						|
#endif
 | 
						|
#include <limits.h>
 | 
						|
#if defined(USE_SDL) || defined(USE_SDL2)
 | 
						|
#include <SDL.h>
 | 
						|
#include <SDL_image.h>
 | 
						|
#include <SDL_endian.h>
 | 
						|
#endif
 | 
						|
#ifndef __no_pnglib__
 | 
						|
#include <png.h>
 | 
						|
#endif
 | 
						|
 | 
						|
#include "gfx2log.h"
 | 
						|
#include "gfx2mem.h"
 | 
						|
#include "buttons.h"
 | 
						|
#include "const.h"
 | 
						|
#include "errors.h"
 | 
						|
#include "global.h"
 | 
						|
#include "keycodes.h"
 | 
						|
#include "io.h"
 | 
						|
#include "loadsave.h"
 | 
						|
#include "loadsavefuncs.h"
 | 
						|
#include "misc.h"
 | 
						|
#include "osdep.h"
 | 
						|
#include "graph.h"
 | 
						|
#include "op_c.h"
 | 
						|
#include "pages.h"
 | 
						|
#include "palette.h"
 | 
						|
#if defined(USE_SDL) || defined(USE_SDL2)
 | 
						|
#include "sdlscreen.h"
 | 
						|
#endif
 | 
						|
#include "screen.h"
 | 
						|
#include "struct.h"
 | 
						|
#include "windows.h"
 | 
						|
#include "engine.h"
 | 
						|
#include "brush.h"
 | 
						|
#include "setup.h"
 | 
						|
#include "filesel.h"
 | 
						|
#include "unicode.h"
 | 
						|
#include "fileformats.h"
 | 
						|
#include "bitcount.h"
 | 
						|
 | 
						|
#if defined(USE_X11) || (defined(SDL_VIDEO_DRIVER_X11) && !defined(NO_X11))
 | 
						|
#include "input.h"
 | 
						|
#if defined(USE_X11)
 | 
						|
extern Display * X11_display;
 | 
						|
extern Window X11_window;
 | 
						|
#endif
 | 
						|
#endif
 | 
						|
 | 
						|
#if defined(__macosx__)
 | 
						|
const void * get_tiff_paste_board(unsigned long * size);
 | 
						|
int set_tiff_paste_board(const void * tiff, unsigned long size);
 | 
						|
#endif
 | 
						|
 | 
						|
 | 
						|
#if defined(USE_SDL) || defined(USE_SDL2)
 | 
						|
// -- SDL_Image -------------------------------------------------------------
 | 
						|
// (TGA, BMP, PNM, XPM, XCF, PCX, GIF, JPG, TIF, IFF, PNG, ICO)
 | 
						|
void Load_SDL_Image(T_IO_Context *);
 | 
						|
#endif
 | 
						|
 | 
						|
// -- Recoil ----------------------------------------------------------------
 | 
						|
// 8bits and 16bits computer graphics
 | 
						|
#ifndef NORECOIL
 | 
						|
void Load_Recoil_Image(T_IO_Context *);
 | 
						|
#endif
 | 
						|
 | 
						|
// clipboard
 | 
						|
static void Load_ClipBoard_Image(T_IO_Context *);
 | 
						|
static void Save_ClipBoard_Image(T_IO_Context *);
 | 
						|
 | 
						|
 | 
						|
// ENUM     Name  TestFunc LoadFunc SaveFunc PalOnly Comment Layers Ext Exts
 | 
						|
const T_Format File_formats[] = {
 | 
						|
  {FORMAT_ALL_IMAGES, "(all)", NULL, NULL, NULL, 0, 0, 0, "",
 | 
						|
    "gif;png;bmp;2bp;pcx;pkm;iff;lbm;ilbm;sham;ham;ham6;ham8;acbm;pic;anim;img;sci;scq;scf;scn;sco;cel;"
 | 
						|
    "pi1;pc1;pi2;pc2;pi3;pc3;neo;tny;tn1;tn2;tn3;tn4;ca1;ca2;ca3;"
 | 
						|
    "c64;p64;a64;pi;rp;aas;art;dd;iph;ipt;hpc;ocp;koa;koala;fli;bml;cdu;prg;pmg;rpm;"
 | 
						|
    "gpx;"
 | 
						|
    "cpc;scr;win;pph;cm5;go1;"
 | 
						|
    "hgr;dhgr;"
 | 
						|
    "grb;grob;"
 | 
						|
    "sc2;"
 | 
						|
    "tga;pnm;xpm;xcf;jpg;jpeg;tif;tiff;ico;ic2;cur;info;flc;bin;map"},
 | 
						|
  {FORMAT_ALL_PALETTES, "(pal)", NULL, NULL, NULL, 1, 0, 0, "", "kcf;pal;gpl"},
 | 
						|
  {FORMAT_ALL_FILES, "(*.*)", NULL, NULL, NULL, 0, 0, 0, "", "*"},
 | 
						|
  {FORMAT_GIF, " gif", Test_GIF, Load_GIF, Save_GIF, 0, 1, 1, "gif", "gif"},
 | 
						|
#ifndef __no_pnglib__
 | 
						|
  {FORMAT_PNG, " png", Test_PNG, Load_PNG, Save_PNG, 0, 1, 0, "png", "png"},
 | 
						|
#endif
 | 
						|
  {FORMAT_BMP, " bmp", Test_BMP, Load_BMP, Save_BMP, 0, 0, 0, "bmp", "bmp;2bp"},
 | 
						|
  {FORMAT_PCX, " pcx", Test_PCX, Load_PCX, Save_PCX, 0, 0, 0, "pcx", "pcx"},
 | 
						|
  {FORMAT_PKM, " pkm", Test_PKM, Load_PKM, Save_PKM, 0, 1, 0, "pkm", "pkm"},
 | 
						|
  {FORMAT_LBM, " lbm", Test_LBM, Load_IFF, Save_IFF, 0, 1, 0, "iff", "iff;lbm;ilbm;sham;ham;ham6;ham8;anim"},
 | 
						|
  {FORMAT_PBM, " pbm", Test_PBM, Load_IFF, Save_IFF, 0, 1, 0, "iff", "iff;pbm;lbm"},
 | 
						|
  {FORMAT_ACBM," acbm",Test_ACBM,Load_IFF, NULL,     0, 1, 0, "iff", "iff;pic;acbm"},
 | 
						|
  {FORMAT_IMG, " img", Test_IMG, Load_IMG, Save_IMG, 0, 0, 0, "img", "img"},
 | 
						|
  {FORMAT_SCx, " sc?", Test_SCx, Load_SCx, Save_SCx, 0, 0, 0, "sc?", "sci;scq;scf;scn;sco"},
 | 
						|
  {FORMAT_PI1, " pi1", Test_PI1, Load_PI1, Save_PI1, 0, 0, 0, "pi1", "pi1;pi2;pi3"},
 | 
						|
  {FORMAT_PC1, " pc1", Test_PC1, Load_PC1, Save_PC1, 0, 0, 0, "pc1", "pc1;pc2;pc3"},
 | 
						|
  {FORMAT_CA1, " ca1", Test_CA1, Load_CA1, Save_CA1, 0, 0, 0, "ca1", "ca1;ca2;ca3"},
 | 
						|
  {FORMAT_TNY, " tny", Test_TNY, Load_TNY, Save_TNY, 0, 0, 0, "tny", "tny;tn1;tn2;tn3;tn4"},
 | 
						|
  {FORMAT_CEL, " cel", Test_CEL, Load_CEL, Save_CEL, 0, 0, 0, "cel", "cel"},
 | 
						|
  {FORMAT_NEO, " neo", Test_NEO, Load_NEO, Save_NEO, 0, 0, 0, "neo", "neo"},
 | 
						|
  {FORMAT_KCF, " kcf", Test_KCF, Load_KCF, Save_KCF, 1, 0, 0, "kcf", "kcf"},
 | 
						|
  {FORMAT_PAL, " pal", Test_PAL, Load_PAL, Save_PAL, 1, 0, 0, "pal", "pal"},
 | 
						|
  {FORMAT_GPL, " gpl", Test_GPL, Load_GPL, Save_GPL, 1, 0, 0, "gpl", "gpl"},
 | 
						|
  {FORMAT_C64, " c64", Test_C64, Load_C64, Save_C64, 0, 1, 1, "c64",
 | 
						|
    "c64;p64;a64;pi;rp;aas;art;dd;iph;ipt;hpc;ocp;koa;koala;fli;bml;cdu;pmg;rpm"},
 | 
						|
  {FORMAT_PRG, " prg", Test_PRG, Load_PRG, Save_PRG, 0, 0, 1, "prg", "prg"},
 | 
						|
  {FORMAT_GPX, " gpx", Test_GPX, Load_GPX, NULL,     0, 0, 0, "gpx", "gpx"},
 | 
						|
  {FORMAT_SCR, " cpc", Test_SCR, Load_SCR, Save_SCR, 0, 0, 0, "scr", "cpc;scr;win"},
 | 
						|
  {FORMAT_CM5, " cm5", Test_CM5, Load_CM5, Save_CM5, 0, 0, 1, "cm5", "cm5"},
 | 
						|
  {FORMAT_PPH, " pph", Test_PPH, Load_PPH, Save_PPH, 0, 0, 1, "pph", "pph"},
 | 
						|
  {FORMAT_GOS, " go1", Test_GOS, Load_GOS, Save_GOS, 0, 0, 0, "go1", "go1"},
 | 
						|
  {FORMAT_XPM, " xpm", NULL,     NULL,     Save_XPM, 0, 0, 0, "xpm", "xpm"},
 | 
						|
  {FORMAT_ICO, " ico", Test_ICO, Load_ICO, Save_ICO, 0, 0, 0, "ico", "ico;ic2;cur"},
 | 
						|
  {FORMAT_INFO," info",Test_INFO,Load_INFO,NULL,     0, 0, 0, "info", "info"},
 | 
						|
  {FORMAT_FLI, " flc", Test_FLI, Load_FLI, NULL,     0, 0, 0, "flc", "flc;fli;dat"},
 | 
						|
  {FORMAT_MOTO," moto",Test_MOTO,Load_MOTO,Save_MOTO,0, 1, 0, "bin", "bin;map"},
 | 
						|
  {FORMAT_MSX, " msx", Test_MSX, Load_MSX, Save_MSX, 0, 0, 0, "sc2", "sc2"},
 | 
						|
  {FORMAT_HGR, " hgr", Test_HGR, Load_HGR, Save_HGR, 0, 0, 1, "hgr", "hgr;dhgr;bin"},
 | 
						|
#ifndef __no_tifflib__
 | 
						|
  {FORMAT_TIFF," tiff",Test_TIFF,Load_TIFF,Save_TIFF,0, 1, 1, "tif", "tif;tiff"},
 | 
						|
#endif
 | 
						|
  {FORMAT_GRB, " grb", Test_GRB, Load_GRB, NULL,     0, 0, 0, "grb", "grb;grob"},
 | 
						|
  {FORMAT_MISC,"misc.",NULL,     NULL,     NULL,     0, 0, 0, "",    "tga;pnm;xpm;xcf;jpg;jpeg;tif;tiff"},
 | 
						|
};
 | 
						|
 | 
						|
/// Total number of known file formats
 | 
						|
unsigned int Nb_known_formats(void)
 | 
						|
{
 | 
						|
  return sizeof(File_formats)/sizeof(File_formats[0]);
 | 
						|
}
 | 
						|
 | 
						|
/// Set the color of a pixel (on load)
 | 
						|
void Set_pixel(T_IO_Context *context, short x_pos, short y_pos, byte color)
 | 
						|
{
 | 
						|
  // Clipping
 | 
						|
  if ((x_pos>=context->Width) || (y_pos>=context->Height))
 | 
						|
    return;
 | 
						|
 | 
						|
  switch (context->Type)
 | 
						|
  {
 | 
						|
    // Chargement des pixels dans l'écran principal
 | 
						|
    case CONTEXT_MAIN_IMAGE:
 | 
						|
      Pixel_in_current_screen(x_pos,y_pos,color);
 | 
						|
      break;
 | 
						|
 | 
						|
    // Chargement des pixels dans la brosse
 | 
						|
    case CONTEXT_BRUSH:
 | 
						|
      //Pixel_in_brush(x_pos,y_pos,color);
 | 
						|
      *(context->Buffer_image + y_pos * context->Pitch + x_pos)=color;
 | 
						|
      break;
 | 
						|
 | 
						|
    // Chargement des pixels dans la preview
 | 
						|
    case CONTEXT_PREVIEW:
 | 
						|
      // Skip pixels of transparent index if :
 | 
						|
      // it's a layer above the first one
 | 
						|
      if (color == context->Transparent_color && context->Current_layer > 0)
 | 
						|
        break;
 | 
						|
 | 
						|
      if (((x_pos % context->Preview_factor_X)==0) && ((y_pos % context->Preview_factor_Y)==0))
 | 
						|
      {
 | 
						|
        // Tag the color as 'used'
 | 
						|
        context->Preview_usage[color]=1;
 | 
						|
 | 
						|
        // Store pixel
 | 
						|
        if (context->Ratio == PIXEL_WIDE &&
 | 
						|
          Pixel_ratio != PIXEL_WIDE &&
 | 
						|
          Pixel_ratio != PIXEL_WIDE2)
 | 
						|
        {
 | 
						|
          context->Preview_bitmap[x_pos/context->Preview_factor_X*2 + (y_pos/context->Preview_factor_Y)*PREVIEW_WIDTH*Menu_factor_X]=color;
 | 
						|
          context->Preview_bitmap[x_pos/context->Preview_factor_X*2+1 + (y_pos/context->Preview_factor_Y)*PREVIEW_WIDTH*Menu_factor_X]=color;
 | 
						|
        }
 | 
						|
        else if (context->Ratio == PIXEL_TALL &&
 | 
						|
          Pixel_ratio != PIXEL_TALL &&
 | 
						|
          Pixel_ratio != PIXEL_TALL2 &&
 | 
						|
          Pixel_ratio != PIXEL_TALL3)
 | 
						|
        {
 | 
						|
          context->Preview_bitmap[x_pos/context->Preview_factor_X + (y_pos/context->Preview_factor_Y*2)*PREVIEW_WIDTH*Menu_factor_X]=color;
 | 
						|
          context->Preview_bitmap[x_pos/context->Preview_factor_X + (y_pos/context->Preview_factor_Y*2+1)*PREVIEW_WIDTH*Menu_factor_X]=color;
 | 
						|
        }
 | 
						|
        else
 | 
						|
          context->Preview_bitmap[x_pos/context->Preview_factor_X + (y_pos/context->Preview_factor_Y)*PREVIEW_WIDTH*Menu_factor_X]=color;
 | 
						|
      }
 | 
						|
 | 
						|
      break;
 | 
						|
 | 
						|
    // Load pixels into a Surface
 | 
						|
    case CONTEXT_SURFACE:
 | 
						|
      if (x_pos>=0 && y_pos>=0 && x_pos<context->Surface->w && y_pos<context->Surface->h)
 | 
						|
        Set_GFX2_Surface_pixel(context->Surface, x_pos, y_pos, color);
 | 
						|
      break;
 | 
						|
 | 
						|
    case CONTEXT_PALETTE:
 | 
						|
    case CONTEXT_PREVIEW_PALETTE:
 | 
						|
      break;
 | 
						|
  }
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
void Fill_canvas(T_IO_Context *context, byte color)
 | 
						|
{
 | 
						|
  switch (context->Type)
 | 
						|
  {
 | 
						|
    case CONTEXT_PREVIEW:
 | 
						|
      if (context->Current_layer!=0)
 | 
						|
        return;
 | 
						|
      memset(context->Preview_bitmap, color, PREVIEW_WIDTH*PREVIEW_HEIGHT*Menu_factor_X*Menu_factor_Y);
 | 
						|
      break;
 | 
						|
    case CONTEXT_MAIN_IMAGE:
 | 
						|
      memset(
 | 
						|
        Main.backups->Pages->Image[Main.current_layer].Pixels,
 | 
						|
        color,
 | 
						|
        Main.backups->Pages->Width*Main.backups->Pages->Height);
 | 
						|
      break;
 | 
						|
    case CONTEXT_BRUSH:
 | 
						|
      memset(context->Buffer_image, color, (long)context->Height*context->Pitch);
 | 
						|
      break;
 | 
						|
    case CONTEXT_SURFACE:
 | 
						|
      break;
 | 
						|
    case CONTEXT_PALETTE:
 | 
						|
    case CONTEXT_PREVIEW_PALETTE:
 | 
						|
      break;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/// Chargement des pixels dans le buffer 24b
 | 
						|
void Set_pixel_24b(T_IO_Context *context, short x_pos, short y_pos, byte r, byte g, byte b)
 | 
						|
{
 | 
						|
  byte color;
 | 
						|
 | 
						|
  // Clipping
 | 
						|
  if (x_pos<0 || y_pos<0 || x_pos>=context->Width || y_pos>=context->Height)
 | 
						|
    return;
 | 
						|
 | 
						|
  switch(context->Type)
 | 
						|
  {
 | 
						|
    case CONTEXT_MAIN_IMAGE:
 | 
						|
    case CONTEXT_BRUSH:
 | 
						|
    case CONTEXT_SURFACE:
 | 
						|
      {
 | 
						|
        int index;
 | 
						|
 | 
						|
        index=(y_pos*context->Width)+x_pos;
 | 
						|
        context->Buffer_image_24b[index].R=r;
 | 
						|
        context->Buffer_image_24b[index].G=g;
 | 
						|
        context->Buffer_image_24b[index].B=b;
 | 
						|
      }
 | 
						|
      break;
 | 
						|
 | 
						|
    case CONTEXT_PREVIEW:
 | 
						|
 | 
						|
      if (((x_pos % context->Preview_factor_X)==0) && ((y_pos % context->Preview_factor_Y)==0))
 | 
						|
      {
 | 
						|
        color=((r >> 5) << 5) |
 | 
						|
                ((g >> 5) << 2) |
 | 
						|
                ((b >> 6));
 | 
						|
 | 
						|
        // Tag the color as 'used'
 | 
						|
        context->Preview_usage[color]=1;
 | 
						|
 | 
						|
        context->Preview_bitmap[x_pos/context->Preview_factor_X + (y_pos/context->Preview_factor_Y)*PREVIEW_WIDTH*Menu_factor_X]=color;
 | 
						|
      }
 | 
						|
      break;
 | 
						|
 | 
						|
    case CONTEXT_PREVIEW_PALETTE:
 | 
						|
    case CONTEXT_PALETTE:
 | 
						|
      // In a palette, there are no pixels!
 | 
						|
      break;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
// Création d'une palette fake
 | 
						|
void Set_palette_fake_24b(T_Palette palette)
 | 
						|
{
 | 
						|
  int color;
 | 
						|
 | 
						|
  // Génération de la palette
 | 
						|
  for (color=0;color<256;color++)
 | 
						|
  {
 | 
						|
    palette[color].R=((color & 0xE0)>>5)<<5;
 | 
						|
    palette[color].G=((color & 0x1C)>>2)<<5;
 | 
						|
    palette[color].B=((color & 0x03)>>0)<<6;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void Set_frame_duration(T_IO_Context *context, int duration)
 | 
						|
{
 | 
						|
  switch(context->Type)
 | 
						|
  {
 | 
						|
    case CONTEXT_MAIN_IMAGE:
 | 
						|
      Main.backups->Pages->Image[context->Current_layer].Duration = duration;
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      break;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
int Get_frame_duration(T_IO_Context *context)
 | 
						|
{
 | 
						|
  switch(context->Type)
 | 
						|
  {
 | 
						|
    case CONTEXT_MAIN_IMAGE:
 | 
						|
      return Main.backups->Pages->Image[context->Current_layer].Duration;
 | 
						|
    default:
 | 
						|
      return 0;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void Set_image_mode(T_IO_Context *context, enum IMAGE_MODES mode)
 | 
						|
{
 | 
						|
  if (context->Type == CONTEXT_MAIN_IMAGE)
 | 
						|
  {
 | 
						|
    Main.backups->Pages->Image_mode = mode;
 | 
						|
    //Switch_layer_mode(mode);
 | 
						|
    Update_screen_targets();
 | 
						|
    if (mode > IMAGE_MODE_ANIMATION)
 | 
						|
      Selected_Constraint_Mode = mode;
 | 
						|
    // update the "FX" button state
 | 
						|
    Draw_menu_button(BUTTON_EFFECTS,Any_effect_active());
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
enum IMAGE_MODES Get_image_mode(T_IO_Context *context)
 | 
						|
{
 | 
						|
  if (context->Type == CONTEXT_MAIN_IMAGE)
 | 
						|
    return Main.backups->Pages->Image_mode;
 | 
						|
  return IMAGE_MODE_LAYERED;
 | 
						|
}
 | 
						|
 | 
						|
///
 | 
						|
/// Generic allocation and similar stuff, done at beginning of image load,
 | 
						|
/// as soon as size is known.
 | 
						|
void Pre_load(T_IO_Context *context, short width, short height, long file_size, int format, enum PIXEL_RATIO ratio, byte bpp)
 | 
						|
{
 | 
						|
  char  str[10];
 | 
						|
  byte truecolor;
 | 
						|
 | 
						|
  if (width < 0 || width > 9999 || height < 0 || height > 9999)
 | 
						|
  {
 | 
						|
    File_error = 1;
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  if (bpp == 0)
 | 
						|
    bpp = 8;  // default to 8bits
 | 
						|
  truecolor = (bpp > 8) ? 1 : 0;
 | 
						|
  context->bpp = bpp;
 | 
						|
  context->Pitch = width; // default
 | 
						|
  context->Width = width;
 | 
						|
  context->Height = height;
 | 
						|
  context->Ratio = ratio;
 | 
						|
  context->Nb_layers = 1;
 | 
						|
  context->Transparent_color=0;
 | 
						|
  context->Background_transparent=0;
 | 
						|
 | 
						|
  switch(context->Type)
 | 
						|
  {
 | 
						|
    // Preview
 | 
						|
    case CONTEXT_PREVIEW:
 | 
						|
      // Préparation du chargement d'une preview:
 | 
						|
 | 
						|
      context->Preview_bitmap=calloc(1, PREVIEW_WIDTH*PREVIEW_HEIGHT*Menu_factor_X*Menu_factor_Y);
 | 
						|
      if (!context->Preview_bitmap)
 | 
						|
        File_error=1;
 | 
						|
 | 
						|
      // Affichage des données "Image size:"
 | 
						|
      memcpy(str, "VERY BIG!", 10); // default string
 | 
						|
      if (context->Original_width != 0)
 | 
						|
      {
 | 
						|
        if (context->Original_width < 10000 && context->Original_height < 10000)
 | 
						|
          snprintf(str, sizeof(str), "%4hux%4hu", context->Original_width, context->Original_height);
 | 
						|
      }
 | 
						|
      else if ((width<10000) && (height<10000))
 | 
						|
      {
 | 
						|
        snprintf(str, sizeof(str), "%4hux%4hu", width, height);
 | 
						|
      }
 | 
						|
      Print_in_window(101,59,str,MC_Black,MC_Light);
 | 
						|
      snprintf(str, sizeof(str), "%2dbpp", bpp);
 | 
						|
      Print_in_window(181,59,str,MC_Black,MC_Light);
 | 
						|
 | 
						|
      // Affichage de la taille du fichier
 | 
						|
      if (file_size<1048576)
 | 
						|
      {
 | 
						|
        // Le fichier fait moins d'un Mega, on affiche sa taille direct
 | 
						|
        Num2str(file_size,str,7);
 | 
						|
      }
 | 
						|
      else if (((file_size+512)/1024)<100000)
 | 
						|
      {
 | 
						|
        // Le fichier fait plus d'un Mega, on peut afficher sa taille en Ko
 | 
						|
        Num2str((file_size+512)/1024,str,5);
 | 
						|
        strcpy(str+5,"KB");
 | 
						|
      }
 | 
						|
      else
 | 
						|
      {
 | 
						|
        // Le fichier fait plus de 100 Mega octets (cas très rare :))
 | 
						|
        memcpy(str,"LARGE!!",8);
 | 
						|
      }
 | 
						|
      Print_in_window(236,59,str,MC_Black,MC_Light);
 | 
						|
 | 
						|
      // Affichage du vrai format
 | 
						|
      Print_in_window( 59,59,Get_fileformat(format)->Label,MC_Black,MC_Light);
 | 
						|
 | 
						|
      // On efface le commentaire précédent
 | 
						|
      Window_rectangle(45,70,32*8,8,MC_Light);
 | 
						|
 | 
						|
      // Calcul des données nécessaires à l'affichage de la preview:
 | 
						|
      if (ratio == PIXEL_WIDE &&
 | 
						|
          Pixel_ratio != PIXEL_WIDE &&
 | 
						|
          Pixel_ratio != PIXEL_WIDE2)
 | 
						|
        width*=2;
 | 
						|
      else if (ratio == PIXEL_TALL &&
 | 
						|
          Pixel_ratio != PIXEL_TALL &&
 | 
						|
          Pixel_ratio != PIXEL_TALL2 &&
 | 
						|
          Pixel_ratio != PIXEL_TALL3)
 | 
						|
        height*=2;
 | 
						|
 | 
						|
      context->Preview_factor_X=Round_div_max(width,120*Menu_factor_X);
 | 
						|
      context->Preview_factor_Y=Round_div_max(height, 80*Menu_factor_Y);
 | 
						|
 | 
						|
      if ( (!Config.Maximize_preview) && (context->Preview_factor_X!=context->Preview_factor_Y) )
 | 
						|
      {
 | 
						|
        if (context->Preview_factor_X>context->Preview_factor_Y)
 | 
						|
          context->Preview_factor_Y=context->Preview_factor_X;
 | 
						|
        else
 | 
						|
          context->Preview_factor_X=context->Preview_factor_Y;
 | 
						|
      }
 | 
						|
 | 
						|
      context->Preview_pos_X=Window_pos_X+183*Menu_factor_X;
 | 
						|
      context->Preview_pos_Y=Window_pos_Y+ 95*Menu_factor_Y;
 | 
						|
 | 
						|
      // On nettoie la zone où va s'afficher la preview:
 | 
						|
      Window_rectangle(183,95,PREVIEW_WIDTH,PREVIEW_HEIGHT,MC_Light);
 | 
						|
 | 
						|
      // Un update pour couvrir les 4 zones: 3 libellés plus le commentaire
 | 
						|
      Update_window_area(45,48,256,30);
 | 
						|
      // Zone de preview
 | 
						|
      Update_window_area(183,95,PREVIEW_WIDTH,PREVIEW_HEIGHT);
 | 
						|
      break;
 | 
						|
 | 
						|
    // Other loading
 | 
						|
    case CONTEXT_MAIN_IMAGE:
 | 
						|
      if (Backup_new_image(1,width,height))
 | 
						|
      {
 | 
						|
        // La nouvelle page a pu être allouée, elle est pour l'instant pleine
 | 
						|
        // de 0s. Elle fait Main_image_width de large.
 | 
						|
        // Normalement tout va bien, tout est sous contrôle...
 | 
						|
 | 
						|
        // Load into layer 0, by default.
 | 
						|
        context->Nb_layers=1;
 | 
						|
        Main.current_layer=0;
 | 
						|
        Main.layers_visible=1<<0;
 | 
						|
        Set_loading_layer(context,0);
 | 
						|
 | 
						|
        // Remove previous comment, unless we load just a palette
 | 
						|
        if (! Get_fileformat(context->Format)->Palette_only)
 | 
						|
          context->Comment[0]='\0';
 | 
						|
      }
 | 
						|
      else
 | 
						|
      {
 | 
						|
        // Afficher un message d'erreur
 | 
						|
        // Pour être sûr que ce soit lisible.
 | 
						|
        Compute_optimal_menu_colors(context->Palette);
 | 
						|
        Message_out_of_memory();
 | 
						|
        File_error=1; // 1 => On n'a pas perdu l'image courante
 | 
						|
      }
 | 
						|
      break;
 | 
						|
 | 
						|
    case CONTEXT_BRUSH:
 | 
						|
      context->Buffer_image = (byte *)GFX2_malloc(width*height);
 | 
						|
      if (! context->Buffer_image)
 | 
						|
      {
 | 
						|
        File_error=3;
 | 
						|
        return;
 | 
						|
      }
 | 
						|
      context->Target_address=context->Buffer_image;
 | 
						|
 | 
						|
      break;
 | 
						|
 | 
						|
    case CONTEXT_SURFACE:
 | 
						|
      context->Surface = New_GFX2_Surface(width, height);
 | 
						|
      if (! context->Surface)
 | 
						|
      {
 | 
						|
        File_error=1;
 | 
						|
        return;
 | 
						|
      }
 | 
						|
      //context->Pitch = context->Surface->pitch;
 | 
						|
      //context->Target_address = context->Surface->pixels;
 | 
						|
      break;
 | 
						|
 | 
						|
    case CONTEXT_PALETTE:
 | 
						|
    case CONTEXT_PREVIEW_PALETTE:
 | 
						|
      // In a palette, there are no pixels!
 | 
						|
      break;
 | 
						|
  }
 | 
						|
 | 
						|
  if (File_error)
 | 
						|
    return;
 | 
						|
 | 
						|
  // Extra process for truecolor images
 | 
						|
  if (truecolor)
 | 
						|
  {
 | 
						|
    switch(context->Type)
 | 
						|
    {
 | 
						|
      case CONTEXT_MAIN_IMAGE:
 | 
						|
      case CONTEXT_BRUSH:
 | 
						|
      case CONTEXT_SURFACE:
 | 
						|
        // Allocate 24bit buffer
 | 
						|
        context->Buffer_image_24b =
 | 
						|
          (T_Components *)GFX2_malloc(width*height*sizeof(T_Components));
 | 
						|
        if (!context->Buffer_image_24b)
 | 
						|
        {
 | 
						|
          // Print an error message
 | 
						|
          // The following is to be sure the message is readable
 | 
						|
          Compute_optimal_menu_colors(context->Palette);
 | 
						|
          Message_out_of_memory();
 | 
						|
          File_error=1;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
 | 
						|
      case CONTEXT_PREVIEW:
 | 
						|
        // 3:3:2 "True Color" palette will be loaded lated in context->Palette
 | 
						|
        // There can be an image palette used to decode the true color picture
 | 
						|
        // Such as for HAM ILBM pictures
 | 
						|
        break;
 | 
						|
 | 
						|
      case CONTEXT_PALETTE:
 | 
						|
    case CONTEXT_PREVIEW_PALETTE:
 | 
						|
        // In a palette, there are no pixels!
 | 
						|
        break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/////////////////////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
// -- Charger n'importe connu quel type de fichier d'image (ou palette) -----
 | 
						|
void Load_image(T_IO_Context *context)
 | 
						|
{
 | 
						|
  unsigned int index; // index de balayage des formats
 | 
						|
  const T_Format *format = &(File_formats[FORMAT_ALL_FILES+1]); // Format du fichier à charger
 | 
						|
  int i;
 | 
						|
  byte old_cursor_shape;
 | 
						|
  FILE * f;
 | 
						|
 | 
						|
  // Not sure it's the best place...
 | 
						|
  context->Color_cycles=0;
 | 
						|
 | 
						|
  // On place par défaut File_error à vrai au cas où on ne sache pas
 | 
						|
  // charger le format du fichier:
 | 
						|
  File_error=1;
 | 
						|
 | 
						|
  if (context->Format == FORMAT_CLIPBOARD)
 | 
						|
  {
 | 
						|
    Load_ClipBoard_Image(context);
 | 
						|
    if (File_error != 0 && context->Format == FORMAT_CLIPBOARD)
 | 
						|
    {
 | 
						|
      Error(0);
 | 
						|
      return;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  if (context->Format != FORMAT_CLIPBOARD)
 | 
						|
  {
 | 
						|
    if (context->File_name == NULL)
 | 
						|
    {
 | 
						|
      GFX2_Log(GFX2_ERROR, "Load_Image() called with NULL file name\n");
 | 
						|
      Error(0);
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    f = Open_file_read(context);
 | 
						|
    if (f == NULL)
 | 
						|
    {
 | 
						|
      GFX2_Log(GFX2_WARNING, "Cannot open file for reading\n");
 | 
						|
      Error(0);
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    if (context->Format > FORMAT_ALL_FILES)
 | 
						|
    {
 | 
						|
      format = Get_fileformat(context->Format);
 | 
						|
      if (format->Test)
 | 
						|
        format->Test(context, f);
 | 
						|
    }
 | 
						|
 | 
						|
    if (File_error)
 | 
						|
    {
 | 
						|
      //  Sinon, on va devoir scanner les différents formats qu'on connait pour
 | 
						|
      // savoir à quel format est le fichier:
 | 
						|
      for (index=0; index < Nb_known_formats(); index++)
 | 
						|
      {
 | 
						|
        format = Get_fileformat(index);
 | 
						|
        // Loadable format
 | 
						|
        if (format->Test == NULL)
 | 
						|
          continue;
 | 
						|
 | 
						|
        fseek(f, 0, SEEK_SET); // rewind
 | 
						|
        // On appelle le testeur du format:
 | 
						|
        format->Test(context, f);
 | 
						|
        // On s'arrête si le fichier est au bon format:
 | 
						|
        if (File_error==0)
 | 
						|
          break;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    fclose(f);
 | 
						|
 | 
						|
    if (File_error)
 | 
						|
    {
 | 
						|
      context->Format = DEFAULT_FILEFORMAT;
 | 
						|
      // try with recoil
 | 
						|
#ifndef NORECOIL
 | 
						|
      Load_Recoil_Image(context);
 | 
						|
      if (File_error)
 | 
						|
#endif
 | 
						|
#if defined(USE_SDL) || defined(USE_SDL2)
 | 
						|
      {
 | 
						|
        // Last try: with SDL_image
 | 
						|
        Load_SDL_Image(context);
 | 
						|
      }
 | 
						|
 | 
						|
      if (File_error)
 | 
						|
#endif
 | 
						|
      {
 | 
						|
        // Sinon, l'appelant sera au courant de l'échec grace à File_error;
 | 
						|
        // et si on s'apprêtait à faire un chargement définitif de l'image (pas
 | 
						|
        // une preview), alors on flash l'utilisateur.
 | 
						|
        //if (Pixel_load_function!=Pixel_load_in_preview)
 | 
						|
        //  Error(0);
 | 
						|
        return;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    else
 | 
						|
    // Si on a su déterminer avec succès le format du fichier:
 | 
						|
    {
 | 
						|
      context->Format = format->Identifier;
 | 
						|
      // On peut charger le fichier:
 | 
						|
      // Dans certains cas il est possible que le chargement plante
 | 
						|
      // après avoir modifié la palette. TODO
 | 
						|
      format->Load(context);
 | 
						|
    }
 | 
						|
 | 
						|
    if (File_error>0)
 | 
						|
    {
 | 
						|
      GFX2_Log(GFX2_WARNING, "Unable to load file %s (error %d)! format:%s\n", context->File_name, File_error, format->Label);
 | 
						|
      if (context->Type!=CONTEXT_SURFACE)
 | 
						|
        Error(0);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  // Post-load
 | 
						|
 | 
						|
  if (context->Buffer_image_24b)
 | 
						|
  {
 | 
						|
    // On vient de charger une image 24b
 | 
						|
    if (!File_error)
 | 
						|
    {
 | 
						|
      switch(context->Type)
 | 
						|
      {
 | 
						|
        case CONTEXT_MAIN_IMAGE:
 | 
						|
          // Cas d'un chargement dans l'image
 | 
						|
          old_cursor_shape=Cursor_shape;
 | 
						|
          Hide_cursor();
 | 
						|
          Cursor_shape=CURSOR_SHAPE_HOURGLASS;
 | 
						|
          Display_cursor();
 | 
						|
          Flush_update();
 | 
						|
          if (Convert_24b_bitmap_to_256(Main.backups->Pages->Image[0].Pixels,context->Buffer_image_24b,context->Width,context->Height,context->Palette))
 | 
						|
            File_error=2;
 | 
						|
          Hide_cursor();
 | 
						|
          Cursor_shape=old_cursor_shape;
 | 
						|
          Display_cursor();
 | 
						|
          break;
 | 
						|
 | 
						|
        case CONTEXT_BRUSH:
 | 
						|
          // Cas d'un chargement dans la brosse
 | 
						|
          old_cursor_shape=Cursor_shape;
 | 
						|
          Hide_cursor();
 | 
						|
          Cursor_shape=CURSOR_SHAPE_HOURGLASS;
 | 
						|
          Display_cursor();
 | 
						|
          Flush_update();
 | 
						|
          if (Convert_24b_bitmap_to_256(context->Buffer_image,context->Buffer_image_24b,context->Width,context->Height,context->Palette))
 | 
						|
            File_error=2;
 | 
						|
          Hide_cursor();
 | 
						|
          Cursor_shape=old_cursor_shape;
 | 
						|
          Display_cursor();
 | 
						|
          break;
 | 
						|
 | 
						|
        case CONTEXT_PREVIEW:
 | 
						|
          // in this context, context->Buffer_image_24b is not allocated
 | 
						|
          // pixels are drawn to context->Preview_bitmap
 | 
						|
          break;
 | 
						|
 | 
						|
        case CONTEXT_SURFACE:
 | 
						|
          if (Convert_24b_bitmap_to_256(context->Surface->pixels,context->Buffer_image_24b,context->Width,context->Height,context->Palette))
 | 
						|
          File_error=1;
 | 
						|
          break;
 | 
						|
 | 
						|
 | 
						|
        case CONTEXT_PALETTE:
 | 
						|
        case CONTEXT_PREVIEW_PALETTE:
 | 
						|
         // In a palette, there are no pixels!
 | 
						|
         break;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    free(context->Buffer_image_24b);
 | 
						|
    context->Buffer_image_24b = NULL;
 | 
						|
  }
 | 
						|
  else if (context->Type == CONTEXT_MAIN_IMAGE)
 | 
						|
  {
 | 
						|
    // Non-24b main image: Add menu colors
 | 
						|
    if (Config.Safety_colors)
 | 
						|
    {
 | 
						|
      dword color_usage[256];
 | 
						|
      memset(color_usage,0,sizeof(color_usage));
 | 
						|
      if (Count_used_colors(color_usage)<252)
 | 
						|
      {
 | 
						|
        int gui_index;
 | 
						|
        // From white to black
 | 
						|
        for (gui_index=3; gui_index>=0; gui_index--)
 | 
						|
        {
 | 
						|
          int c;
 | 
						|
          T_Components gui_color;
 | 
						|
 | 
						|
          gui_color=*Favorite_GUI_color(gui_index);
 | 
						|
          // Try find a very close match (ignore last 2 bits)
 | 
						|
          for (c=255; c>=0; c--)
 | 
						|
          {
 | 
						|
            if ((context->Palette[c].R|3) == (gui_color.R|3)
 | 
						|
             && (context->Palette[c].G|3) == (gui_color.G|3)
 | 
						|
             && (context->Palette[c].B|3) == (gui_color.B|3) )
 | 
						|
             break;
 | 
						|
          }
 | 
						|
          if (c<0) // Not found
 | 
						|
          {
 | 
						|
            // Find an unused slot at end of palette
 | 
						|
            for (c=255; c>=0; c--)
 | 
						|
            {
 | 
						|
              if (color_usage[c]==0)
 | 
						|
              {
 | 
						|
                context->Palette[c]=gui_color;
 | 
						|
                // Tag as a used color
 | 
						|
                color_usage[c]=1;
 | 
						|
                break;
 | 
						|
              }
 | 
						|
            }
 | 
						|
          }
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (context->Type == CONTEXT_MAIN_IMAGE)
 | 
						|
  {
 | 
						|
    if ( File_error!=1)
 | 
						|
    {
 | 
						|
      Set_palette(context->Palette);
 | 
						|
      if (format->Palette_only)
 | 
						|
      {
 | 
						|
        // Make a backup step
 | 
						|
        Backup_layers(LAYER_NONE);
 | 
						|
      }
 | 
						|
      // Copy the loaded palette
 | 
						|
      memcpy(Main.palette, context->Palette, sizeof(T_Palette));
 | 
						|
      memcpy(Main.backups->Pages->Palette, context->Palette, sizeof(T_Palette));
 | 
						|
 | 
						|
      // For formats that handle more than just the palette:
 | 
						|
      // Transfer the data to main image.
 | 
						|
      if (!format->Palette_only)
 | 
						|
      {
 | 
						|
        free(Main.backups->Pages->Filename);
 | 
						|
        free(Main.backups->Pages->File_directory);
 | 
						|
        free(Main.backups->Pages->Filename_unicode);
 | 
						|
        Main.backups->Pages->Filename_unicode = NULL;
 | 
						|
        if (context->Original_file_name != NULL
 | 
						|
          && context->Original_file_directory != NULL)
 | 
						|
        {
 | 
						|
          Main.backups->Pages->Filename = context->Original_file_name;  // steal buffer !
 | 
						|
          context->Original_file_name = NULL;
 | 
						|
          Main.backups->Pages->File_directory = context->Original_file_directory;  // steal heap buffer
 | 
						|
          context->Original_file_directory = NULL;
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
          Main.backups->Pages->Filename = strdup(context->File_name);
 | 
						|
          Main.backups->Pages->File_directory = strdup(context->File_directory);
 | 
						|
          if (context->File_name_unicode)
 | 
						|
            Main.backups->Pages->Filename_unicode = Unicode_strdup(context->File_name_unicode);
 | 
						|
        }
 | 
						|
 | 
						|
        // On considère que l'image chargée n'est plus modifiée
 | 
						|
        Main.image_is_modified=0;
 | 
						|
        // Et on documente la variable Main_fileformat avec la valeur:
 | 
						|
        Main.fileformat=format->Identifier;
 | 
						|
 | 
						|
        // already done initially on Backup_with_new_dimensions
 | 
						|
        //Main_image_width= context->Width;
 | 
						|
        //Main_image_height= context->Height;
 | 
						|
 | 
						|
        if (Main.backups->Pages->Image_mode == IMAGE_MODE_ANIMATION)
 | 
						|
        {
 | 
						|
          Main.current_layer = 0;
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
          Main.current_layer = context->Nb_layers - 1;
 | 
						|
          Main.layers_visible = (2<<Main.current_layer)-1;
 | 
						|
        }
 | 
						|
 | 
						|
        // Load the transparency data
 | 
						|
        Main.backups->Pages->Transparent_color = context->Transparent_color;
 | 
						|
        Main.backups->Pages->Background_transparent = context->Background_transparent;
 | 
						|
 | 
						|
        // Correction des dimensions
 | 
						|
        if (Main.image_width<1)
 | 
						|
          Main.image_width=1;
 | 
						|
        if (Main.image_height<1)
 | 
						|
          Main.image_height=1;
 | 
						|
 | 
						|
        // Color cyling ranges:
 | 
						|
        for (i=0; i<16; i++)
 | 
						|
          Main.backups->Pages->Gradients->Range[i].Speed=0;
 | 
						|
        for (i=0; i<context->Color_cycles; i++)
 | 
						|
        {
 | 
						|
          Main.backups->Pages->Gradients->Range[i].Start=context->Cycle_range[i].Start;
 | 
						|
          Main.backups->Pages->Gradients->Range[i].End=context->Cycle_range[i].End;
 | 
						|
          Main.backups->Pages->Gradients->Range[i].Inverse=context->Cycle_range[i].Inverse;
 | 
						|
          Main.backups->Pages->Gradients->Range[i].Speed=context->Cycle_range[i].Speed;
 | 
						|
        }
 | 
						|
 | 
						|
        // Comment
 | 
						|
        strcpy(Main.backups->Pages->Comment, context->Comment);
 | 
						|
 | 
						|
      }
 | 
						|
    }
 | 
						|
    else if (File_error!=1)
 | 
						|
    {
 | 
						|
      // On considère que l'image chargée est encore modifiée
 | 
						|
      Main.image_is_modified=1;
 | 
						|
      // Et on documente la variable Main_fileformat avec la valeur:
 | 
						|
      Main.fileformat=format->Identifier;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      // Dans ce cas, on sait que l'image n'a pas changé, mais ses
 | 
						|
      // paramètres (dimension, palette, ...) si. Donc on les restaures.
 | 
						|
      Download_infos_page_main(Main.backups->Pages);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else if (context->Type == CONTEXT_PALETTE)
 | 
						|
  {
 | 
						|
    if ( File_error!=1)
 | 
						|
    {
 | 
						|
      Set_palette(context->Palette);
 | 
						|
      // Make a backup step
 | 
						|
      Backup_layers(LAYER_NONE);
 | 
						|
      // Copy the loaded palette
 | 
						|
      memcpy(Main.palette, context->Palette, sizeof(T_Palette));
 | 
						|
      memcpy(Main.backups->Pages->Palette, context->Palette, sizeof(T_Palette));
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else if (context->Type == CONTEXT_BRUSH && File_error==0)
 | 
						|
  {
 | 
						|
    // For brushes, Back_color is the transparent color.
 | 
						|
    // Set it before remapping, Remap_brush() will take care of it
 | 
						|
    if (context->Background_transparent)
 | 
						|
      Back_color = context->Transparent_color;
 | 
						|
    if (Realloc_brush(context->Width, context->Height, context->Buffer_image, NULL))
 | 
						|
    {
 | 
						|
      File_error=3;
 | 
						|
      free(context->Buffer_image);
 | 
						|
    }
 | 
						|
    memcpy(Brush_original_palette, context->Palette, sizeof(T_Palette));
 | 
						|
    Remap_brush();
 | 
						|
 | 
						|
    context->Buffer_image = NULL;
 | 
						|
  }
 | 
						|
  else if (context->Type == CONTEXT_SURFACE)
 | 
						|
  {
 | 
						|
    if (File_error == 0)
 | 
						|
    {
 | 
						|
      // Copy the palette
 | 
						|
      memcpy(context->Surface->palette, context->Palette, sizeof(T_Palette));
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else if (context->Type == CONTEXT_PREVIEW || context->Type == CONTEXT_PREVIEW_PALETTE
 | 
						|
    /*&& !context->Buffer_image_24b*/
 | 
						|
    /*&& !Get_fileformat(context->Format)->Palette_only*/)
 | 
						|
  {
 | 
						|
 | 
						|
    // Try to adapt the palette to accomodate the GUI.
 | 
						|
    int c;
 | 
						|
    int count_unused;
 | 
						|
    byte unused_color[4];
 | 
						|
 | 
						|
    if (context->Type == CONTEXT_PREVIEW && context->bpp > 8)
 | 
						|
      Set_palette_fake_24b(context->Palette);
 | 
						|
 | 
						|
    count_unused=0;
 | 
						|
    // Try find 4 unused colors and insert good colors there
 | 
						|
    for (c=255; c>=0 && count_unused<4; c--)
 | 
						|
    {
 | 
						|
      if (!context->Preview_usage[c])
 | 
						|
      {
 | 
						|
        unused_color[count_unused]=c;
 | 
						|
        count_unused++;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    // Found! replace them with some favorites
 | 
						|
    if (count_unused==4)
 | 
						|
    {
 | 
						|
      int gui_index;
 | 
						|
      for (gui_index=0; gui_index<4; gui_index++)
 | 
						|
      {
 | 
						|
        context->Palette[unused_color[gui_index]]=*Favorite_GUI_color(gui_index);
 | 
						|
      }
 | 
						|
    }
 | 
						|
    // All preview display is here
 | 
						|
 | 
						|
    // Update palette and screen first
 | 
						|
    Compute_optimal_menu_colors(context->Palette);
 | 
						|
    Remap_screen_after_menu_colors_change();
 | 
						|
    Set_palette(context->Palette);
 | 
						|
 | 
						|
    // Display palette preview
 | 
						|
    if (Get_fileformat(context->Format)->Palette_only
 | 
						|
        || context->Type == CONTEXT_PREVIEW_PALETTE)
 | 
						|
    {
 | 
						|
      short index;
 | 
						|
 | 
						|
      if (context->Type == CONTEXT_PREVIEW || context->Type == CONTEXT_PREVIEW_PALETTE)
 | 
						|
        for (index=0; index<256; index++)
 | 
						|
          Window_rectangle(183+(index/16)*7,95+(index&15)*5,5,5,index);
 | 
						|
 | 
						|
    }
 | 
						|
    // Display normal image
 | 
						|
    else if (context->Preview_bitmap)
 | 
						|
    {
 | 
						|
      int x_pos,y_pos;
 | 
						|
      int width,height;
 | 
						|
      width=context->Width/context->Preview_factor_X;
 | 
						|
      height=context->Height/context->Preview_factor_Y;
 | 
						|
      if (context->Ratio == PIXEL_WIDE &&
 | 
						|
          Pixel_ratio != PIXEL_WIDE &&
 | 
						|
          Pixel_ratio != PIXEL_WIDE2)
 | 
						|
        width*=2;
 | 
						|
      else if (context->Ratio == PIXEL_TALL &&
 | 
						|
          Pixel_ratio != PIXEL_TALL &&
 | 
						|
          Pixel_ratio != PIXEL_TALL2 &&
 | 
						|
          Pixel_ratio != PIXEL_TALL3)
 | 
						|
        height*=2;
 | 
						|
 | 
						|
      for (y_pos=0; y_pos<height;y_pos++)
 | 
						|
        for (x_pos=0; x_pos<width;x_pos++)
 | 
						|
        {
 | 
						|
          byte color=context->Preview_bitmap[x_pos+y_pos*PREVIEW_WIDTH*Menu_factor_X];
 | 
						|
 | 
						|
          // Skip transparent if image has transparent background.
 | 
						|
          if (color == context->Transparent_color && context->Background_transparent)
 | 
						|
            color=MC_Window;
 | 
						|
 | 
						|
          Pixel(context->Preview_pos_X+x_pos,
 | 
						|
                context->Preview_pos_Y+y_pos,
 | 
						|
                color);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    // Refresh modified part
 | 
						|
    Update_window_area(183,95,PREVIEW_WIDTH,PREVIEW_HEIGHT);
 | 
						|
 | 
						|
    // Preview comment
 | 
						|
    Print_in_window(45,70,context->Comment,MC_Black,MC_Light);
 | 
						|
    //Update_window_area(45,70,32*8,8);
 | 
						|
 | 
						|
  }
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
// -- Sauver n'importe quel type connu de fichier d'image (ou palette) ------
 | 
						|
void Save_image(T_IO_Context *context)
 | 
						|
{
 | 
						|
  const T_Format *format;
 | 
						|
 | 
						|
  // On place par défaut File_error à vrai au cas où on ne sache pas
 | 
						|
  // sauver le format du fichier: (Est-ce vraiment utile??? Je ne crois pas!)
 | 
						|
  File_error=1;
 | 
						|
  format = Get_fileformat(context->Format);
 | 
						|
 | 
						|
  switch (context->Type)
 | 
						|
  {
 | 
						|
    case CONTEXT_MAIN_IMAGE:
 | 
						|
      if ((context->Format == FORMAT_CLIPBOARD || !format->Supports_layers)
 | 
						|
        && (Main.backups->Pages->Nb_layers > 1)
 | 
						|
        && (!format->Palette_only))
 | 
						|
      {
 | 
						|
        if (Main.backups->Pages->Image_mode == IMAGE_MODE_ANIMATION)
 | 
						|
        {
 | 
						|
          if (! Confirmation_box("This format doesn't support\nanimation and will save only\ncurrent frame. Proceed?"))
 | 
						|
          {
 | 
						|
            // File_error is already set to 1.
 | 
						|
            return;
 | 
						|
          }
 | 
						|
          // current layer
 | 
						|
          context->Nb_layers=1;
 | 
						|
          context->Target_address=Main.backups->Pages->Image[Main.current_layer].Pixels;
 | 
						|
        }
 | 
						|
        else // all other layer-based formats
 | 
						|
        {
 | 
						|
          int clicked_button;
 | 
						|
 | 
						|
          Open_window(208,100,"Format warning");
 | 
						|
          Print_in_window( 8, 20,"This file format doesn't",MC_Black,MC_Light);
 | 
						|
          Print_in_window( 8, 30,"support layers.",MC_Black,MC_Light);
 | 
						|
 | 
						|
          Window_set_normal_button(23,44, 162,14,"Save flattened copy",0,1,KEY_NONE); // 1
 | 
						|
          Window_set_normal_button(23,62, 162,14,"Save current frame" ,0,1,KEY_NONE); // 2
 | 
						|
          Window_set_normal_button(23,80, 162,14,"Cancel"             ,0,1,KEY_ESC);  // 3
 | 
						|
          Update_window_area(0,0,Window_width, Window_height);
 | 
						|
          Display_cursor();
 | 
						|
          do
 | 
						|
          {
 | 
						|
            clicked_button=Window_clicked_button();
 | 
						|
            // Some help on file formats ?
 | 
						|
            //if (Is_shortcut(Key,0x100+BUTTON_HELP))
 | 
						|
            //{
 | 
						|
            //  Key=0;
 | 
						|
            //  Window_help(???, NULL);
 | 
						|
            //}
 | 
						|
          }
 | 
						|
          while (clicked_button<=0);
 | 
						|
          Close_window();
 | 
						|
          Display_cursor();
 | 
						|
          switch(clicked_button)
 | 
						|
          {
 | 
						|
            case 1: // flatten
 | 
						|
              context->Nb_layers=1;
 | 
						|
              context->Target_address=Main.visible_image.Image;
 | 
						|
              break;
 | 
						|
            case 2: // current layer
 | 
						|
              context->Nb_layers=1;
 | 
						|
              context->Target_address=Main.backups->Pages->Image[Main.current_layer].Pixels;
 | 
						|
              break;
 | 
						|
            default: // Cancel
 | 
						|
              // File_error is already set to 1.
 | 
						|
              return;
 | 
						|
          }
 | 
						|
        }
 | 
						|
      }
 | 
						|
      break;
 | 
						|
 | 
						|
    case CONTEXT_BRUSH:
 | 
						|
      break;
 | 
						|
 | 
						|
    case CONTEXT_PREVIEW:
 | 
						|
    case CONTEXT_PREVIEW_PALETTE:
 | 
						|
      break;
 | 
						|
 | 
						|
    case CONTEXT_SURFACE:
 | 
						|
      break;
 | 
						|
 | 
						|
    case CONTEXT_PALETTE:
 | 
						|
      // In a palette, there are no pixels!
 | 
						|
      break;
 | 
						|
  }
 | 
						|
 | 
						|
  if (context->Format == FORMAT_CLIPBOARD)
 | 
						|
    Save_ClipBoard_Image(context);
 | 
						|
  else
 | 
						|
  {
 | 
						|
    if (format->Save)
 | 
						|
      format->Save(context);
 | 
						|
  }
 | 
						|
 | 
						|
  if (File_error)
 | 
						|
  {
 | 
						|
    Error(0);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
#if defined(USE_SDL) || defined(USE_SDL2)
 | 
						|
void Load_SDL_Image(T_IO_Context *context)
 | 
						|
{
 | 
						|
  char * filename;  // full path
 | 
						|
  word x_pos,y_pos;
 | 
						|
  // long file_size;
 | 
						|
  dword pixel;
 | 
						|
  long file_size;
 | 
						|
  SDL_Surface * surface;
 | 
						|
 | 
						|
  filename = Filepath_append_to_dir(context->File_directory, context->File_name);
 | 
						|
  file_size = File_length(filename);
 | 
						|
  File_error = 0;
 | 
						|
 | 
						|
  surface = IMG_Load(filename);
 | 
						|
  free(filename);
 | 
						|
 | 
						|
  if (!surface)
 | 
						|
  {
 | 
						|
    File_error=1;
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  if (surface->format->BytesPerPixel == 1)
 | 
						|
  {
 | 
						|
    // 8bpp image
 | 
						|
    Pre_load(context, surface->w, surface->h, file_size ,FORMAT_MISC, PIXEL_SIMPLE, 8);
 | 
						|
 | 
						|
    // Read palette
 | 
						|
    if (surface->format->palette)
 | 
						|
    {
 | 
						|
      Get_SDL_Palette(surface->format->palette, context->Palette);
 | 
						|
    }
 | 
						|
 | 
						|
    for (y_pos=0; y_pos<context->Height; y_pos++)
 | 
						|
    {
 | 
						|
      for (x_pos=0; x_pos<context->Width; x_pos++)
 | 
						|
      {
 | 
						|
        Set_pixel(context, x_pos, y_pos, Get_SDL_pixel_8(surface, x_pos, y_pos));
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    {
 | 
						|
      // Hi/Trucolor
 | 
						|
      Pre_load(context, surface->w, surface->h, file_size ,FORMAT_ALL_IMAGES, PIXEL_SIMPLE, 8 * surface->format->BytesPerPixel);
 | 
						|
    }
 | 
						|
 | 
						|
    for (y_pos=0; y_pos<context->Height; y_pos++)
 | 
						|
    {
 | 
						|
      for (x_pos=0; x_pos<context->Width; x_pos++)
 | 
						|
      {
 | 
						|
        pixel = Get_SDL_pixel_hicolor(surface, x_pos, y_pos);
 | 
						|
        Set_pixel_24b(
 | 
						|
          context,
 | 
						|
          x_pos,
 | 
						|
          y_pos,
 | 
						|
          ((pixel & surface->format->Rmask) >> surface->format->Rshift) << surface->format->Rloss,
 | 
						|
          ((pixel & surface->format->Gmask) >> surface->format->Gshift) << surface->format->Gloss,
 | 
						|
          ((pixel & surface->format->Bmask) >> surface->format->Bshift) << surface->format->Bloss);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  SDL_FreeSurface(surface);
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
 | 
						|
T_GFX2_Surface * Load_surface(const char *filename, const char * directory, T_Gradient_array *gradients)
 | 
						|
{
 | 
						|
  T_GFX2_Surface * bmp=NULL;
 | 
						|
  T_IO_Context context;
 | 
						|
 | 
						|
  Init_context_surface(&context, filename, directory);
 | 
						|
  Load_image(&context);
 | 
						|
 | 
						|
  if (context.Surface)
 | 
						|
  {
 | 
						|
    bmp=context.Surface;
 | 
						|
    // Caller wants the gradients:
 | 
						|
    if (gradients != NULL)
 | 
						|
    {
 | 
						|
      int i;
 | 
						|
 | 
						|
      memset(gradients, 0, sizeof(T_Gradient_array));
 | 
						|
      for (i=0; i<context.Color_cycles; i++)
 | 
						|
      {
 | 
						|
        gradients->Range[i].Start=context.Cycle_range[i].Start;
 | 
						|
        gradients->Range[i].End=context.Cycle_range[i].End;
 | 
						|
        gradients->Range[i].Inverse=context.Cycle_range[i].Inverse;
 | 
						|
        gradients->Range[i].Speed=context.Cycle_range[i].Speed;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  Destroy_context(&context);
 | 
						|
 | 
						|
  return bmp;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static void Load_ClipBoard_Image(T_IO_Context * context)
 | 
						|
{
 | 
						|
#ifdef WIN32
 | 
						|
  UINT format;
 | 
						|
  HANDLE clipboard;
 | 
						|
 | 
						|
  if (!OpenClipboard(GFX2_Get_Window_Handle()))
 | 
						|
  {
 | 
						|
    GFX2_Log(GFX2_ERROR, "Failed to open Clipboard\n");
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  format = EnumClipboardFormats(0);
 | 
						|
  while (format != 0)
 | 
						|
  {
 | 
						|
    const char * format_name = NULL;
 | 
						|
    char format_name_buffer[256];
 | 
						|
 | 
						|
    switch (format)
 | 
						|
    {
 | 
						|
      case CF_TEXT:
 | 
						|
        format_name = "TEXT";
 | 
						|
        break;
 | 
						|
      case CF_OEMTEXT:
 | 
						|
        format_name = "OEMTEXT";
 | 
						|
        break;
 | 
						|
      case CF_UNICODETEXT:
 | 
						|
        format_name = "UNICODE TEXT";
 | 
						|
        break;
 | 
						|
#if(WINVER >= 0x0400)
 | 
						|
      case CF_LOCALE:
 | 
						|
        format_name = "Locale identifier";
 | 
						|
        break;
 | 
						|
      case CF_HDROP:
 | 
						|
        format_name = "Drop Handle";
 | 
						|
        break;
 | 
						|
#endif
 | 
						|
      case CF_DIB:
 | 
						|
        format_name = "DIB (BITMAPINFO)";
 | 
						|
        break;
 | 
						|
      case CF_DIBV5:
 | 
						|
        format_name = "DIBV5 (BITMAPV5HEADER)";
 | 
						|
        break;
 | 
						|
      case CF_BITMAP:
 | 
						|
        format_name = "HBITMAP";
 | 
						|
        break;
 | 
						|
      case CF_METAFILEPICT:
 | 
						|
        format_name = "METAFILEPICT";
 | 
						|
        break;
 | 
						|
      case CF_PALETTE:
 | 
						|
        format_name = "Palette";
 | 
						|
        break;
 | 
						|
      case CF_TIFF:
 | 
						|
        format_name = "TIFF";
 | 
						|
        break;
 | 
						|
      case CF_ENHMETAFILE:
 | 
						|
        format_name = "HENMETAFILE";
 | 
						|
        break;
 | 
						|
      default:
 | 
						|
      if (GetClipboardFormatNameA(format, format_name_buffer, sizeof(format_name_buffer)) <= 0)
 | 
						|
        GFX2_Log(GFX2_WARNING, "Failed to get name for clipboard format %u\n", format);
 | 
						|
      else
 | 
						|
        format_name = format_name_buffer;
 | 
						|
    }
 | 
						|
    if (format_name != NULL)
 | 
						|
      GFX2_Log(GFX2_DEBUG, "Available format %5u \"%s\"\n", format, format_name);
 | 
						|
 | 
						|
    format = EnumClipboardFormats(format);  // get next format
 | 
						|
  }
 | 
						|
  clipboard = GetClipboardData(CF_DIB);
 | 
						|
  if (clipboard == NULL)
 | 
						|
  {
 | 
						|
    // Try to load the filename
 | 
						|
    UINT filename_type = RegisterClipboardFormatA("FileName");
 | 
						|
    GFX2_Log(GFX2_INFO, "Failed to get Clipboard in DIB (BITMAPINFO) format\n");
 | 
						|
    if (filename_type == 0)
 | 
						|
      GFX2_Log(GFX2_ERROR, "Failed to register \"FileName\" Clipboard format\n");
 | 
						|
    else
 | 
						|
    {
 | 
						|
      clipboard = GetClipboardData(filename_type);
 | 
						|
      if (clipboard == NULL)
 | 
						|
        GFX2_Log(GFX2_INFO, "Failed to get Clipboard in \"FileName\" format\n");
 | 
						|
      else
 | 
						|
      {
 | 
						|
        const char * filename = (const char *)GlobalLock(clipboard);
 | 
						|
        if (filename == NULL)
 | 
						|
          GFX2_Log(GFX2_ERROR, "GlobalLock() failed error 0x%08x\n", GetLastError());
 | 
						|
        else
 | 
						|
        {
 | 
						|
          GFX2_Log(GFX2_DEBUG, "filename from clipboard : \"%s\"\n", filename);
 | 
						|
          if (File_exists(filename))
 | 
						|
          {
 | 
						|
            free(context->File_name);
 | 
						|
            context->File_name = Extract_filename(NULL, filename);
 | 
						|
            free(context->File_directory);
 | 
						|
            context->File_directory = Extract_path(NULL, filename);
 | 
						|
            context->Format = DEFAULT_FILEFORMAT;
 | 
						|
          }
 | 
						|
          else
 | 
						|
          {
 | 
						|
            GFX2_Log(GFX2_WARNING, "file \"%s\" does not exist\n", filename);
 | 
						|
          }
 | 
						|
          GlobalUnlock(clipboard);
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    // Load the DIB (BITMAPINFO)
 | 
						|
    const PBITMAPINFO bmi = (PBITMAPINFO)GlobalLock(clipboard);
 | 
						|
    if (bmi == NULL)
 | 
						|
      GFX2_Log(GFX2_ERROR, "GlobalLock() failed error 0x%08x\n", GetLastError());
 | 
						|
    else
 | 
						|
    {
 | 
						|
      unsigned long width, height;
 | 
						|
      width = bmi->bmiHeader.biWidth;
 | 
						|
      height = (bmi->bmiHeader.biHeight > 0) ? bmi->bmiHeader.biHeight : -bmi->bmiHeader.biHeight;
 | 
						|
 | 
						|
      GFX2_Log(GFX2_DEBUG, "DIB %ldx%ld planes=%u bpp=%u compression=%u size=%u ClrUsed=%u\n",
 | 
						|
                bmi->bmiHeader.biWidth, bmi->bmiHeader.biHeight,
 | 
						|
                bmi->bmiHeader.biPlanes, bmi->bmiHeader.biBitCount,
 | 
						|
                bmi->bmiHeader.biCompression, bmi->bmiHeader.biSizeImage,
 | 
						|
                bmi->bmiHeader.biClrUsed);
 | 
						|
      if (width > 9999 || height > 9999)
 | 
						|
        GFX2_Log(GFX2_INFO, "Image too big : %lux%lu\n", width, height);
 | 
						|
      else if (bmi->bmiHeader.biCompression == BI_RGB)
 | 
						|
      {
 | 
						|
        unsigned i, color_count;
 | 
						|
        const byte * pixels;
 | 
						|
        unsigned int x, y;
 | 
						|
 | 
						|
        File_error = 0; // have to be set before calling Pre_load()
 | 
						|
        Pre_load(context, width, height, bmi->bmiHeader.biSizeImage, FORMAT_CLIPBOARD, PIXEL_SIMPLE, bmi->bmiHeader.biBitCount);
 | 
						|
 | 
						|
        color_count = bmi->bmiHeader.biClrUsed;
 | 
						|
        if (bmi->bmiHeader.biBitCount <= 8)
 | 
						|
        { // get palette
 | 
						|
          if (color_count == 0)
 | 
						|
            color_count = 1 << bmi->bmiHeader.biBitCount;
 | 
						|
          for (i = 0; i < color_count; i++)
 | 
						|
          {
 | 
						|
            context->Palette[i].R = bmi->bmiColors[i].rgbRed;
 | 
						|
            context->Palette[i].G = bmi->bmiColors[i].rgbGreen;
 | 
						|
            context->Palette[i].B = bmi->bmiColors[i].rgbBlue;
 | 
						|
          }
 | 
						|
        }
 | 
						|
        pixels = (const byte *)(&bmi->bmiColors[color_count]);
 | 
						|
        switch (bmi->bmiHeader.biBitCount)
 | 
						|
        {
 | 
						|
          case 8:
 | 
						|
            for (y = 0; y < height; y++)
 | 
						|
            {
 | 
						|
              const byte * line;
 | 
						|
              if (bmi->bmiHeader.biHeight > 0)
 | 
						|
                line = pixels + (height - y - 1) * ((bmi->bmiHeader.biWidth + 3) & ~3);
 | 
						|
              else
 | 
						|
                line = pixels + y * ((bmi->bmiHeader.biWidth + 3) & ~3);
 | 
						|
              for (x = 0; x < width; x++)
 | 
						|
                Set_pixel(context, x, y, line[x]);
 | 
						|
            }
 | 
						|
            break;
 | 
						|
          case 24:
 | 
						|
            for (y = 0; y < height; y++)
 | 
						|
            {
 | 
						|
              const byte * line = pixels;
 | 
						|
              if (bmi->bmiHeader.biHeight > 0)
 | 
						|
                line += (height - y - 1) * ((bmi->bmiHeader.biWidth * 3 + 3) & ~3);
 | 
						|
              else
 | 
						|
                line += y * ((bmi->bmiHeader.biWidth * 3 + 3) & ~3);
 | 
						|
              for (x = 0; x < width; x++)
 | 
						|
                Set_pixel_24b(context, x, y, line[x*3 + 2], line[x*3 + 1], line[x*3]);
 | 
						|
            }
 | 
						|
            break;
 | 
						|
          case 32:
 | 
						|
            for (y = 0; y < height; y++)
 | 
						|
            {
 | 
						|
              const byte * line;
 | 
						|
              if (bmi->bmiHeader.biHeight > 0)
 | 
						|
                line = pixels + (height - y - 1) * bmi->bmiHeader.biWidth * 4;
 | 
						|
              else
 | 
						|
                line = pixels + y * bmi->bmiHeader.biWidth * 4;
 | 
						|
              for (x = 0; x < width; x++)
 | 
						|
                Set_pixel_24b(context, x, y, line[x*4 + 2], line[x*4 + 1], line[x*4]);
 | 
						|
            }
 | 
						|
            break;
 | 
						|
          default:
 | 
						|
            GFX2_Log(GFX2_ERROR, "Loading %ubpp pictures from Clipboard is not implemented yet!\n", bmi->bmiHeader.biBitCount);
 | 
						|
            File_error = 1;
 | 
						|
        }
 | 
						|
      }
 | 
						|
      else if (bmi->bmiHeader.biCompression == BI_BITFIELDS)
 | 
						|
      {
 | 
						|
        dword r_mask = *((const dword *)&bmi->bmiColors[0]);
 | 
						|
        int r_bits = count_set_bits(r_mask);
 | 
						|
        int r_shift = count_trailing_zeros(r_mask);
 | 
						|
        dword g_mask = *((const dword *)&bmi->bmiColors[1]);
 | 
						|
        int g_bits = count_set_bits(g_mask);
 | 
						|
        int g_shift = count_trailing_zeros(g_mask);
 | 
						|
        dword b_mask = *((const dword *)&bmi->bmiColors[2]);
 | 
						|
        int b_bits = count_set_bits(b_mask);
 | 
						|
        int b_shift = count_trailing_zeros(b_mask);
 | 
						|
        const byte * pixels = (const byte *)&bmi->bmiColors[3];
 | 
						|
        unsigned int bytes_per_pixel = (bmi->bmiHeader.biBitCount + 7) >> 3;
 | 
						|
        int bytes_per_line = (bmi->bmiHeader.biWidth * bytes_per_pixel + 3) & ~3;
 | 
						|
        unsigned int x, y;
 | 
						|
 | 
						|
        GFX2_Log(GFX2_DEBUG, "RGB%d%d%d, masks : %08x %08x %08x\n", r_bits, g_bits, b_bits, r_mask, g_mask, b_mask);
 | 
						|
 | 
						|
        File_error = 0; // have to be set before calling Pre_load()
 | 
						|
        Pre_load(context, width, height, bmi->bmiHeader.biSizeImage, FORMAT_CLIPBOARD, PIXEL_SIMPLE, bmi->bmiHeader.biBitCount);
 | 
						|
 | 
						|
        for (y = 0; y < height; y++)
 | 
						|
        {
 | 
						|
          const byte * ptr;
 | 
						|
          if (bmi->bmiHeader.biHeight > 0)
 | 
						|
            ptr = pixels + (height - y - 1) * bytes_per_line;
 | 
						|
          else
 | 
						|
            ptr = pixels + y * bytes_per_line;
 | 
						|
          for (x = 0; x < width; x++)
 | 
						|
          {
 | 
						|
            dword rgb, r, g, b;
 | 
						|
            switch (bytes_per_pixel)
 | 
						|
            {
 | 
						|
              case 1:
 | 
						|
                rgb = *ptr;
 | 
						|
                break;
 | 
						|
              case 2:
 | 
						|
                rgb = *((const word *)ptr);
 | 
						|
                break;
 | 
						|
              case 3:
 | 
						|
                rgb = ((dword)ptr[2] << 16) | ((dword)ptr[1] << 8) | (dword)ptr[0];
 | 
						|
                break;
 | 
						|
              default:
 | 
						|
                rgb = *((const dword *)ptr);
 | 
						|
            }
 | 
						|
            r = (rgb & r_mask) >> r_shift;
 | 
						|
            r = (r << (8 - r_bits)) | (r >> (2 * r_bits - 8));
 | 
						|
            g = (rgb & g_mask) >> g_shift;
 | 
						|
            g = (g << (8 - g_bits)) | (g >> (2 * g_bits - 8));
 | 
						|
            b = (rgb & b_mask) >> b_shift;
 | 
						|
            b = (b << (8 - b_bits)) | (b >> (2 * b_bits - 8));
 | 
						|
            Set_pixel_24b(context, x, y, r, g, b);
 | 
						|
            ptr += bytes_per_pixel;
 | 
						|
          }
 | 
						|
        }
 | 
						|
      }
 | 
						|
      else
 | 
						|
        GFX2_Log(GFX2_INFO, "Unsupported DIB compression %u\n", bmi->bmiHeader.biCompression);
 | 
						|
      GlobalUnlock(clipboard);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  CloseClipboard();
 | 
						|
#elif defined(__macosx__)
 | 
						|
  unsigned long size;
 | 
						|
  const void * tiff = get_tiff_paste_board(&size);
 | 
						|
 | 
						|
  GFX2_Log(GFX2_DEBUG, "TIFF pasteboard : %p (%lu bytes)\n", tiff, size);
 | 
						|
  if (tiff != NULL)
 | 
						|
    Load_TIFF_from_memory(context, tiff, size);
 | 
						|
 | 
						|
#elif defined(USE_X11) || (defined(SDL_VIDEO_DRIVER_X11) && !defined(NO_X11))
 | 
						|
  int i;
 | 
						|
  Atom selection;
 | 
						|
  Window selection_owner;
 | 
						|
#if defined(SDL_VIDEO_DRIVER_X11)
 | 
						|
  Display * X11_display;
 | 
						|
  Window X11_window;
 | 
						|
  int old_wmevent_state;
 | 
						|
 | 
						|
  if (!GFX2_Get_X11_Display_Window(&X11_display, &X11_window))
 | 
						|
  {
 | 
						|
    GFX2_Log(GFX2_ERROR, "Failed to get X11 display and window\n");
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  if (X11_display == NULL)
 | 
						|
  {
 | 
						|
#if defined(USE_SDL)
 | 
						|
    char video_driver_name[32];
 | 
						|
    GFX2_Log(GFX2_WARNING, "X11 display is NULL. X11 is needed for Copy/Paste. SDL video driver is currently %s\n", SDL_VideoDriverName(video_driver_name, sizeof(video_driver_name)));
 | 
						|
#elif defined(USE_SDL2)
 | 
						|
    GFX2_Log(GFX2_WARNING, "X11 display is NULL. X11 is needed for Copy/Paste. SDL video driver is currently %s\n", SDL_GetCurrentVideoDriver());
 | 
						|
#endif
 | 
						|
    return;
 | 
						|
  }
 | 
						|
#endif
 | 
						|
 | 
						|
  selection = XInternAtom(X11_display, "CLIPBOARD", False);
 | 
						|
  selection_owner = XGetSelectionOwner(X11_display, selection);
 | 
						|
  if (selection_owner == None)
 | 
						|
  {
 | 
						|
    GFX2_Log(GFX2_INFO, "No owner for the X11 \"CLIPBOARD\" selection\n");
 | 
						|
    return;
 | 
						|
  }
 | 
						|
#if defined(USE_SDL) || defined(USE_SDL2)
 | 
						|
  // Enable processing of X11 events
 | 
						|
  old_wmevent_state = SDL_EventState(SDL_SYSWMEVENT, SDL_QUERY);
 | 
						|
  SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
 | 
						|
#endif
 | 
						|
 | 
						|
  // "TARGETS" is a special content type. The selection owner will
 | 
						|
  // respond with a list of supported type. We will then choose our
 | 
						|
  // prefered type and ask for it.
 | 
						|
  // see Handle_SelectionNotify()
 | 
						|
  // We could ask directly for "image/png" or "image/tiff" but that is
 | 
						|
  // not sure this is supported by the selection owner.
 | 
						|
  XConvertSelection(X11_display, selection, XInternAtom(X11_display, "TARGETS", False),
 | 
						|
                    XInternAtom(X11_display, "GFX2_CLIP", False), /* Property */
 | 
						|
                    X11_window, CurrentTime);
 | 
						|
  // wait for the event to be received. 500ms maximum
 | 
						|
  for(i = 0; X11_clipboard == NULL && i < 25; i++)
 | 
						|
  {
 | 
						|
    Get_input(20);
 | 
						|
  }
 | 
						|
#if defined(USE_SDL) || defined(USE_SDL2)
 | 
						|
  SDL_EventState(SDL_SYSWMEVENT, old_wmevent_state);
 | 
						|
#endif
 | 
						|
 | 
						|
  switch(X11_clipboard_type)
 | 
						|
  {
 | 
						|
    case X11_CLIPBOARD_NONE:
 | 
						|
      GFX2_Log(GFX2_INFO, "Unable to retrieve X11 \"CLIPBOARD\" selection in a supported format. X11_clipboard=%p\n", X11_clipboard);
 | 
						|
      break;
 | 
						|
#ifndef __no_pnglib__
 | 
						|
    case X11_CLIPBOARD_PNG:
 | 
						|
      if (png_sig_cmp((byte *)X11_clipboard, 0, 8) == 0)
 | 
						|
      {
 | 
						|
        File_error = 0;
 | 
						|
        Load_PNG_Sub(context, NULL, X11_clipboard, X11_clipboard_size);
 | 
						|
      }
 | 
						|
      else
 | 
						|
        GFX2_Log(GFX2_WARNING, "Failed to load PNG Clipboard\n");
 | 
						|
      break;
 | 
						|
#endif
 | 
						|
#ifndef __no_tifflib__
 | 
						|
    case X11_CLIPBOARD_TIFF:
 | 
						|
      Load_TIFF_from_memory(context, X11_clipboard, X11_clipboard_size);
 | 
						|
      if (File_error != 0)
 | 
						|
        GFX2_Log(GFX2_WARNING, "Failed to load TIFF Clipboard\n");
 | 
						|
      break;
 | 
						|
#endif
 | 
						|
    case X11_CLIPBOARD_UTF8STRING:
 | 
						|
    case X11_CLIPBOARD_URILIST:
 | 
						|
      {
 | 
						|
        char * tmp_path = NULL;
 | 
						|
        char * p;
 | 
						|
        p = strchr(X11_clipboard, '\r');
 | 
						|
        if (p != NULL)
 | 
						|
          *p = '\0';
 | 
						|
        p = strchr(X11_clipboard, '\n');
 | 
						|
        if (p != NULL)
 | 
						|
          *p = '\0';
 | 
						|
        p = X11_clipboard;
 | 
						|
        if (strncmp(p, "file://", 7) == 0)
 | 
						|
        {
 | 
						|
          tmp_path = GFX2_malloc(strlen(p) + 1);
 | 
						|
          if (tmp_path == NULL)
 | 
						|
            break;
 | 
						|
          p += 7;
 | 
						|
          for (i = 0; *p; i++)
 | 
						|
          {
 | 
						|
            // URLdecode
 | 
						|
            if (p[0] == '%' && p[1] && p[2])
 | 
						|
            {
 | 
						|
              p++;
 | 
						|
              tmp_path[i] = ((*p >= 'A' && *p <= 'F') ? *p - 'A' + 10 : *p - '0') << 4;
 | 
						|
              p++;
 | 
						|
              tmp_path[i] += ((*p >= 'A' && *p <= 'F') ? *p - 'A' + 10 : *p - '0');
 | 
						|
            }
 | 
						|
            else
 | 
						|
              tmp_path[i] = *p;
 | 
						|
            p++;
 | 
						|
          }
 | 
						|
          tmp_path[i] = '\0';
 | 
						|
          p = tmp_path;
 | 
						|
        }
 | 
						|
        if (File_exists(p))
 | 
						|
        {
 | 
						|
          free(context->File_name);
 | 
						|
          context->File_name = Extract_filename(NULL, p);
 | 
						|
          free(context->File_directory);
 | 
						|
          context->File_directory = Extract_path(NULL, p);
 | 
						|
          context->Format = DEFAULT_FILEFORMAT;
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
          GFX2_Log(GFX2_WARNING, "not a filename : \"%s\"\n", p);
 | 
						|
        }
 | 
						|
        free(tmp_path);
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      GFX2_Log(GFX2_WARNING, "Unsupported Clipboard format %d\n", (int)X11_clipboard_type);
 | 
						|
  }
 | 
						|
  free(X11_clipboard);
 | 
						|
  X11_clipboard = NULL;
 | 
						|
  X11_clipboard_size = 0;
 | 
						|
  X11_clipboard_type = X11_CLIPBOARD_NONE;
 | 
						|
 | 
						|
#else
 | 
						|
  (void)context;
 | 
						|
  GFX2_Log(GFX2_ERROR, "Load_ClipBoard_Image() not implemented on this platform yet\n");
 | 
						|
  File_error = 1;
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
static void Save_ClipBoard_Image(T_IO_Context * context)
 | 
						|
{
 | 
						|
#ifdef WIN32
 | 
						|
  if (!OpenClipboard(GFX2_Get_Window_Handle()))
 | 
						|
  {
 | 
						|
    GFX2_Log(GFX2_ERROR, "Failed to open Clipboard\n");
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  if (!EmptyClipboard())
 | 
						|
    GFX2_Log(GFX2_ERROR, "EmptyClipboard() failed error 0x%08x\n", GetLastError());
 | 
						|
  else
 | 
						|
  {
 | 
						|
    int line_width = (context->Width + 3) & ~3;
 | 
						|
    HGLOBAL clipboard = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD) + line_width * context->Height);
 | 
						|
    if (clipboard == NULL)
 | 
						|
      GFX2_Log(GFX2_ERROR, "GlobalAlloc() failed error 0x%08x\n", GetLastError());
 | 
						|
    else
 | 
						|
    {
 | 
						|
      PBITMAPINFO bmi = (PBITMAPINFO)GlobalLock(clipboard);
 | 
						|
      if (bmi == NULL)
 | 
						|
        GFX2_Log(GFX2_ERROR, "GlobalLock() failed error 0x%08x\n", GetLastError());
 | 
						|
      else
 | 
						|
      {
 | 
						|
        byte * pixels;
 | 
						|
        unsigned int i;
 | 
						|
        int x, y;
 | 
						|
 | 
						|
        bmi->bmiHeader.biSize = sizeof(bmi->bmiHeader);
 | 
						|
        bmi->bmiHeader.biWidth = context->Width;
 | 
						|
        bmi->bmiHeader.biHeight = context->Height;
 | 
						|
        bmi->bmiHeader.biPlanes = 1;
 | 
						|
        bmi->bmiHeader.biBitCount = 8;
 | 
						|
        bmi->bmiHeader.biCompression = BI_RGB;
 | 
						|
        bmi->bmiHeader.biSizeImage = context->Height * line_width;
 | 
						|
        //bmi->bmiHeader.biXPelsPerMeter;
 | 
						|
        //bmi->bmiHeader.biYPelsPerMeter;
 | 
						|
        for (i = 0; i < 256; i++)
 | 
						|
        {
 | 
						|
          bmi->bmiColors[i].rgbRed = context->Palette[i].R;
 | 
						|
          bmi->bmiColors[i].rgbGreen = context->Palette[i].G;
 | 
						|
          bmi->bmiColors[i].rgbBlue = context->Palette[i].B;
 | 
						|
          bmi->bmiColors[i].rgbReserved = 0;
 | 
						|
        }
 | 
						|
        pixels = (byte *)&bmi->bmiColors[256] + bmi->bmiHeader.biSizeImage;
 | 
						|
        for (y = 0; y < context->Height; y++)
 | 
						|
        {
 | 
						|
          pixels -= line_width;
 | 
						|
          for (x = 0; x < context->Width; x++)
 | 
						|
            pixels[x] = Get_pixel(context, x, y);
 | 
						|
        }
 | 
						|
        GlobalUnlock(clipboard);
 | 
						|
        if (SetClipboardData(CF_DIB, clipboard) == NULL)
 | 
						|
          GFX2_Log(GFX2_ERROR, "SetClipboardData() failed error 0x%08x\n", GetLastError());
 | 
						|
        else
 | 
						|
          File_error = 0;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  CloseClipboard();
 | 
						|
#elif defined(__macosx__)
 | 
						|
  void * tiff = NULL;
 | 
						|
  unsigned long size = 0;
 | 
						|
 | 
						|
  Save_TIFF_to_memory(context, &tiff, &size);
 | 
						|
  if (File_error == 0 && tiff != NULL)
 | 
						|
  {
 | 
						|
    if(!set_tiff_paste_board(tiff, size))
 | 
						|
      File_error = 1;
 | 
						|
  }
 | 
						|
  free(tiff);
 | 
						|
 | 
						|
#elif defined(USE_X11) || (defined(SDL_VIDEO_DRIVER_X11) && !defined(NO_X11))
 | 
						|
  Atom selection;
 | 
						|
#if defined(SDL_VIDEO_DRIVER_X11)
 | 
						|
  Display * X11_display;
 | 
						|
  Window X11_window;
 | 
						|
  //int old_wmevent_state;
 | 
						|
 | 
						|
  if (!GFX2_Get_X11_Display_Window(&X11_display, &X11_window))
 | 
						|
  {
 | 
						|
    GFX2_Log(GFX2_ERROR, "Failed to get X11 display and window\n");
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  if (X11_display == NULL)
 | 
						|
  {
 | 
						|
#if defined(USE_SDL)
 | 
						|
    char video_driver_name[32];
 | 
						|
    GFX2_Log(GFX2_WARNING, "X11 display is NULL. X11 is needed for Copy/Paste. SDL video driver is currently %s\n", SDL_VideoDriverName(video_driver_name, sizeof(video_driver_name)));
 | 
						|
#elif defined(USE_SDL2)
 | 
						|
    GFX2_Log(GFX2_WARNING, "X11 display is NULL. X11 is needed for Copy/Paste. SDL video driver is currently %s\n", SDL_GetCurrentVideoDriver());
 | 
						|
#endif
 | 
						|
    return;
 | 
						|
  }
 | 
						|
#endif
 | 
						|
 | 
						|
  File_error = 0;
 | 
						|
  if (X11_clipboard != NULL)
 | 
						|
  {
 | 
						|
    free(X11_clipboard);
 | 
						|
    X11_clipboard = NULL;
 | 
						|
    X11_clipboard_size = 0;
 | 
						|
  }
 | 
						|
  Save_PNG_Sub(context, NULL, &X11_clipboard, &X11_clipboard_size);
 | 
						|
  if (!File_error)
 | 
						|
  {
 | 
						|
#if defined(USE_SDL) || defined(USE_SDL2)
 | 
						|
    SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
 | 
						|
#endif
 | 
						|
    selection = XInternAtom(X11_display, "CLIPBOARD", False);
 | 
						|
    XSetSelectionOwner(X11_display, selection, X11_window, CurrentTime);
 | 
						|
  }
 | 
						|
#else
 | 
						|
  (void)context;
 | 
						|
  GFX2_Log(GFX2_ERROR, "Save_ClipBoard_Image() not implemented on this platform yet\n");
 | 
						|
  File_error = 1;
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
/// Saves an image.
 | 
						|
/// This routine will only be called when all hope is lost, memory thrashed, etc
 | 
						|
/// It's the last chance to save anything, but the code has to be extremely
 | 
						|
/// careful, anything could happen.
 | 
						|
/// The chosen format is IMG since it's extremely simple, difficult to make it
 | 
						|
/// create an unusable image.
 | 
						|
void Emergency_backup(const char *fname, byte *source, int width, int height, T_Palette *palette)
 | 
						|
{
 | 
						|
  char * filename; // Full path name
 | 
						|
  FILE *file;
 | 
						|
  short x_pos,y_pos;
 | 
						|
  T_IMG_Header IMG_header;
 | 
						|
 | 
						|
  if (width == 0 || height == 0 || source == NULL)
 | 
						|
    return;
 | 
						|
 | 
						|
  // Open the file
 | 
						|
  filename = Filepath_append_to_dir(Config_directory, fname);
 | 
						|
  file = fopen(filename,"wb");
 | 
						|
  free(filename);
 | 
						|
  if (!file)
 | 
						|
    return;
 | 
						|
 | 
						|
  memcpy(IMG_header.Filler1,"\x01\x00\x47\x12\x6D\xB0",6);
 | 
						|
  memset(IMG_header.Filler2,0,118);
 | 
						|
  IMG_header.Filler2[4]=0xFF;
 | 
						|
  IMG_header.Filler2[22]=64; // Lo(Longueur de la signature)
 | 
						|
  IMG_header.Filler2[23]=0;  // Hi(Longueur de la signature)
 | 
						|
  memcpy(IMG_header.Filler2+23,"GRAFX2 by SunsetDesign (IMG format taken from PV (c)W.Wiedmann)",64);
 | 
						|
 | 
						|
  if (!Write_bytes(file,IMG_header.Filler1,6) ||
 | 
						|
      !Write_word_le(file,width) ||
 | 
						|
      !Write_word_le(file,height) ||
 | 
						|
      !Write_bytes(file,IMG_header.Filler2,118) ||
 | 
						|
      !Write_bytes(file,palette,sizeof(T_Palette)))
 | 
						|
    {
 | 
						|
      fclose(file);
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
  for (y_pos=0; ((y_pos<height) && (!File_error)); y_pos++)
 | 
						|
    for (x_pos=0; x_pos<width; x_pos++)
 | 
						|
      if (!Write_byte(file,*(source+y_pos*width+x_pos)))
 | 
						|
      {
 | 
						|
        fclose(file);
 | 
						|
        return;
 | 
						|
      }
 | 
						|
 | 
						|
  // Ouf, sauvé
 | 
						|
  fclose(file);
 | 
						|
}
 | 
						|
 | 
						|
void Image_emergency_backup()
 | 
						|
{
 | 
						|
  if (Main.backups && Main.backups->Pages && Main.backups->Pages->Nb_layers == 1)
 | 
						|
    Emergency_backup(SAFETYBACKUP_PREFIX_A "999999" BACKUP_FILE_EXTENSION,Main_screen, Main.image_width, Main.image_height, &Main.palette);
 | 
						|
  if (Spare.backups && Spare.backups->Pages && Spare.backups->Pages->Nb_layers == 1)
 | 
						|
    Emergency_backup(SAFETYBACKUP_PREFIX_B "999999" BACKUP_FILE_EXTENSION,Spare.visible_image.Image, Spare.image_width, Spare.image_height, &Spare.palette);
 | 
						|
}
 | 
						|
 | 
						|
const T_Format * Get_fileformat(byte format)
 | 
						|
{
 | 
						|
  unsigned int i;
 | 
						|
  const T_Format * safe_default = File_formats;
 | 
						|
 | 
						|
  for (i=0; i < Nb_known_formats(); i++)
 | 
						|
  {
 | 
						|
    if (File_formats[i].Identifier == format)
 | 
						|
      return &(File_formats[i]);
 | 
						|
 | 
						|
    if (File_formats[i].Identifier == FORMAT_GIF)
 | 
						|
      safe_default=&(File_formats[i]);
 | 
						|
  }
 | 
						|
  // Normally impossible to reach this point, unless called with an invalid
 | 
						|
  // enum....
 | 
						|
  return safe_default;
 | 
						|
}
 | 
						|
 | 
						|
/// Query the color of a pixel (to save)
 | 
						|
byte Get_pixel(T_IO_Context *context, short x, short y)
 | 
						|
{
 | 
						|
  return *(context->Target_address + y*context->Pitch + x);
 | 
						|
}
 | 
						|
 | 
						|
/// Cleans up resources
 | 
						|
void Destroy_context(T_IO_Context *context)
 | 
						|
{
 | 
						|
  free(context->Buffer_image_24b);
 | 
						|
  free(context->Buffer_image);
 | 
						|
  free(context->Preview_bitmap);
 | 
						|
  free(context->File_name);
 | 
						|
  free(context->File_name_unicode);
 | 
						|
  free(context->File_directory);
 | 
						|
  free(context->Original_file_directory);
 | 
						|
  free(context->Original_file_name);
 | 
						|
  memset(context, 0, sizeof(T_IO_Context));
 | 
						|
}
 | 
						|
 | 
						|
/// Setup for loading a preview in fileselector
 | 
						|
void Init_context_preview(T_IO_Context * context, const char *file_name, const char *file_directory)
 | 
						|
{
 | 
						|
  memset(context, 0, sizeof(T_IO_Context));
 | 
						|
 | 
						|
  context->Type = CONTEXT_PREVIEW;
 | 
						|
  context->File_name = file_name != NULL ? strdup(file_name) : NULL;
 | 
						|
  context->File_directory = file_directory != NULL ? strdup(file_directory) : NULL;
 | 
						|
}
 | 
						|
 | 
						|
// Setup for loading/saving an intermediate backup
 | 
						|
void Init_context_backup_image(T_IO_Context * context, const char *file_name, const char *file_directory)
 | 
						|
{
 | 
						|
  Init_context_layered_image(context, file_name, file_directory);
 | 
						|
}
 | 
						|
 | 
						|
/// Setup for loading/saving the current main image
 | 
						|
void Init_context_layered_image(T_IO_Context * context, const char *file_name, const char *file_directory)
 | 
						|
{
 | 
						|
  int i;
 | 
						|
 | 
						|
  memset(context, 0, sizeof(T_IO_Context));
 | 
						|
 | 
						|
  context->Type = CONTEXT_MAIN_IMAGE;
 | 
						|
  context->File_name = file_name != NULL ? strdup(file_name) : NULL;
 | 
						|
  context->File_directory = file_directory != NULL ? strdup(file_directory) : NULL;
 | 
						|
  context->Format = Main.fileformat;
 | 
						|
  memcpy(context->Palette, Main.palette, sizeof(T_Palette));
 | 
						|
  context->Width = Main.image_width;
 | 
						|
  context->Height = Main.image_height;
 | 
						|
  context->Nb_layers = Main.backups->Pages->Nb_layers;
 | 
						|
  strcpy(context->Comment, Main.backups->Pages->Comment);
 | 
						|
  context->Transparent_color=Main.backups->Pages->Transparent_color;
 | 
						|
  context->Background_transparent=Main.backups->Pages->Background_transparent;
 | 
						|
  context->Ratio = Pixel_ratio;
 | 
						|
  context->Target_address=Main.backups->Pages->Image[0].Pixels;
 | 
						|
  context->Pitch=Main.image_width;
 | 
						|
 | 
						|
  // Color cyling ranges:
 | 
						|
  for (i=0; i<16; i++)
 | 
						|
  {
 | 
						|
    if (Main.backups->Pages->Gradients->Range[i].Start!=Main.backups->Pages->Gradients->Range[i].End)
 | 
						|
    {
 | 
						|
      context->Cycle_range[context->Color_cycles].Start=Main.backups->Pages->Gradients->Range[i].Start;
 | 
						|
      context->Cycle_range[context->Color_cycles].End=Main.backups->Pages->Gradients->Range[i].End;
 | 
						|
      context->Cycle_range[context->Color_cycles].Inverse=Main.backups->Pages->Gradients->Range[i].Inverse;
 | 
						|
      context->Cycle_range[context->Color_cycles].Speed=Main.backups->Pages->Gradients->Range[i].Speed;
 | 
						|
      context->Color_cycles++;
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/// Setup for loading/saving the flattened version of current main image
 | 
						|
//void Init_context_flat_image(T_IO_Context * context, char *file_name, char *file_directory)
 | 
						|
//{
 | 
						|
 | 
						|
//}
 | 
						|
 | 
						|
/// Setup for loading/saving the user's brush
 | 
						|
void Init_context_brush(T_IO_Context * context, const char *file_name, const char *file_directory)
 | 
						|
{
 | 
						|
  memset(context, 0, sizeof(T_IO_Context));
 | 
						|
 | 
						|
  context->Type = CONTEXT_BRUSH;
 | 
						|
  context->File_name = file_name != NULL ? strdup(file_name) : NULL;
 | 
						|
  context->File_directory = file_directory != NULL ? strdup(file_directory) : NULL;
 | 
						|
  context->Format = Brush_fileformat;
 | 
						|
  // Use main screen's palette
 | 
						|
  memcpy(context->Palette, Main.palette, sizeof(T_Palette));
 | 
						|
  context->Width = Brush_width;
 | 
						|
  context->Height = Brush_height;
 | 
						|
  context->Nb_layers = 1;
 | 
						|
  context->Transparent_color=Back_color;  // Transparent color for brushes
 | 
						|
  context->Background_transparent=1;
 | 
						|
  context->Ratio=PIXEL_SIMPLE;
 | 
						|
  context->Target_address=Brush;
 | 
						|
  context->Pitch=Brush_width;
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
// Setup for loading an image into a new GFX2 surface.
 | 
						|
void Init_context_surface(T_IO_Context * context, const char *file_name, const char *file_directory)
 | 
						|
{
 | 
						|
  memset(context, 0, sizeof(T_IO_Context));
 | 
						|
 | 
						|
  context->Type = CONTEXT_SURFACE;
 | 
						|
  context->File_name = file_name != NULL ? strdup(file_name) : NULL;
 | 
						|
  context->File_directory = file_directory != NULL ? strdup(file_directory) : NULL;
 | 
						|
  context->Format = DEFAULT_FILEFORMAT;
 | 
						|
  // context->Palette
 | 
						|
  // context->Width
 | 
						|
  // context->Height
 | 
						|
  context->Nb_layers = 1;
 | 
						|
  context->Transparent_color=0;
 | 
						|
  context->Background_transparent=0;
 | 
						|
  context->Ratio=PIXEL_SIMPLE;
 | 
						|
  //context->Target_address
 | 
						|
  //context->Pitch
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
/// Function to call when need to switch layers.
 | 
						|
void Set_saving_layer(T_IO_Context *context, int layer)
 | 
						|
{
 | 
						|
  context->Current_layer = layer;
 | 
						|
 | 
						|
  if (context->Type == CONTEXT_MAIN_IMAGE)
 | 
						|
  {
 | 
						|
    if (context->Nb_layers==1 && Main.backups->Pages->Nb_layers!=1)
 | 
						|
    {
 | 
						|
      // Context is set to saving a single layer: do nothing
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      context->Target_address=Main.backups->Pages->Image[layer].Pixels;
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/// Function to call when need to switch layers.
 | 
						|
void Set_loading_layer(T_IO_Context *context, int layer)
 | 
						|
{
 | 
						|
  context->Current_layer = layer;
 | 
						|
 | 
						|
  if (context->Type == CONTEXT_MAIN_IMAGE)
 | 
						|
  {
 | 
						|
    // This awful thing is the part that happens on load
 | 
						|
    while (layer >= context->Nb_layers)
 | 
						|
    {
 | 
						|
      if (Add_layer(Main.backups, layer))
 | 
						|
      {
 | 
						|
        // Failure to add a layer on load:
 | 
						|
        // Position on last layer
 | 
						|
        layer = context->Nb_layers-1;
 | 
						|
        break;
 | 
						|
      }
 | 
						|
      context->Nb_layers = Main.backups->Pages->Nb_layers;
 | 
						|
      Main.layers_visible = (2<<layer)-1;
 | 
						|
    }
 | 
						|
    Main.current_layer = layer;
 | 
						|
    context->Target_address=Main.backups->Pages->Image[layer].Pixels;
 | 
						|
 | 
						|
    Update_pixel_renderer();
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
// ============================================
 | 
						|
// Safety backups
 | 
						|
// ============================================
 | 
						|
 | 
						|
 | 
						|
typedef struct T_String_list
 | 
						|
{
 | 
						|
  char * String;
 | 
						|
  struct T_String_list * Next;
 | 
						|
} T_String_list;
 | 
						|
 | 
						|
/// A list of files, used for scanning a directory
 | 
						|
T_String_list *Backups_main = NULL;
 | 
						|
/// A list of files, used for scanning a directory
 | 
						|
T_String_list *Backups_spare = NULL;
 | 
						|
 | 
						|
 | 
						|
// Settings for safety backup (frequency, numbers, etc)
 | 
						|
 | 
						|
const int Rotation_safety_backup = 8;
 | 
						|
 | 
						|
const int Min_interval_for_safety_backup = 30000;
 | 
						|
const int Min_edits_for_safety_backup = 10;
 | 
						|
 | 
						|
const int Max_interval_for_safety_backup = 60000;
 | 
						|
const int Max_edits_for_safety_backup = 30;
 | 
						|
 | 
						|
///
 | 
						|
/// Adds a file to Backups_main or Backups_spare lists, if it's a backup.
 | 
						|
///
 | 
						|
static void Add_backup_file(const char * full_name, const char *file_name)
 | 
						|
{
 | 
						|
  T_String_list ** list;
 | 
						|
  T_String_list * elem;
 | 
						|
  int i;
 | 
						|
  (void)full_name;
 | 
						|
 | 
						|
  // Only files names of the form a0000000.* and b0000000.* are expected
 | 
						|
 | 
						|
  // Check first character
 | 
						|
  if (file_name[0]==Main.safety_backup_prefix)
 | 
						|
    list = &Backups_main;
 | 
						|
  else if (file_name[0]==Spare.safety_backup_prefix)
 | 
						|
    list = &Backups_spare;
 | 
						|
   else {
 | 
						|
    // Not a good file
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  // Check next characters till file extension
 | 
						|
  i = 1;
 | 
						|
  while (file_name[i]!='\0' && file_name[i]!='.')
 | 
						|
  {
 | 
						|
    if (file_name[i]< '0' || file_name[i] > '9')
 | 
						|
    {
 | 
						|
      // Not a good file
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    i++;
 | 
						|
  }
 | 
						|
 | 
						|
  // Add to list (top insertion)
 | 
						|
  elem = (T_String_list *)GFX2_malloc(sizeof(T_String_list));
 | 
						|
  elem->String=strdup(file_name);
 | 
						|
  elem->Next=*list;
 | 
						|
  *list=elem;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/// String comparer for sorting
 | 
						|
int String_compare (const void * a, const void * b)
 | 
						|
{
 | 
						|
  return strcmp(*(char**)a,*(char**)b);
 | 
						|
}
 | 
						|
 | 
						|
///
 | 
						|
/// Reload safety backups, by loading several files in the right order.
 | 
						|
///
 | 
						|
byte Process_backups(T_String_list **list)
 | 
						|
{
 | 
						|
  int nb_files;
 | 
						|
  int i;
 | 
						|
  char ** files_vector;
 | 
						|
  T_String_list *element;
 | 
						|
  byte backup_max_undo_pages;
 | 
						|
 | 
						|
  if (*list == NULL)
 | 
						|
    return 0;
 | 
						|
 | 
						|
  // Save the maximum number of pages
 | 
						|
  // (It's used in Create_new_page() which gets called on each Load_image)
 | 
						|
  backup_max_undo_pages = Config.Max_undo_pages;
 | 
						|
  Config.Max_undo_pages = 99;
 | 
						|
 | 
						|
  // Count files
 | 
						|
  nb_files=0;
 | 
						|
  element=*list;
 | 
						|
  while (element != NULL)
 | 
						|
  {
 | 
						|
    nb_files++;
 | 
						|
    element = element->Next;
 | 
						|
  }
 | 
						|
  // Allocate a vector
 | 
						|
  files_vector = (char **)GFX2_malloc(sizeof(char *) * nb_files);
 | 
						|
// TODO
 | 
						|
  // Copy from list to vector
 | 
						|
  for (i=0;i<nb_files;i++)
 | 
						|
  {
 | 
						|
    T_String_list *next;
 | 
						|
 | 
						|
    files_vector[i]=(*list)->String;
 | 
						|
    next = (*list)->Next;
 | 
						|
    free(*list);
 | 
						|
    *list = next;
 | 
						|
  }
 | 
						|
 | 
						|
  // Sort the vector
 | 
						|
  qsort (files_vector, nb_files , sizeof(char **), String_compare);
 | 
						|
 | 
						|
  for (i=0; i < nb_files; i++)
 | 
						|
  {
 | 
						|
    // Load this file
 | 
						|
    T_IO_Context context;
 | 
						|
 | 
						|
    Init_context_backup_image(&context, files_vector[i], Config_directory);
 | 
						|
    // Provide buffers to read original location
 | 
						|
    Load_image(&context);
 | 
						|
    Main.image_is_modified=1;
 | 
						|
    Destroy_context(&context);
 | 
						|
    Redraw_layered_image();
 | 
						|
    Display_all_screen();
 | 
						|
  }
 | 
						|
 | 
						|
  // Done with the vector
 | 
						|
  for (i=0; i < nb_files; i++)
 | 
						|
  {
 | 
						|
    free(files_vector[i]);
 | 
						|
  }
 | 
						|
  free(files_vector);
 | 
						|
  files_vector = NULL;
 | 
						|
 | 
						|
  // Restore the maximum number of pages
 | 
						|
  Config.Max_undo_pages = backup_max_undo_pages;
 | 
						|
 | 
						|
  return nb_files;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/// Global indicator that tells if the safety backup system is active
 | 
						|
byte Safety_backup_active = 0;
 | 
						|
 | 
						|
///
 | 
						|
/// Checks if there are any pending safety backups, and then opens them.
 | 
						|
/// @return 0 if no problem, -1 if the backup system cannot be activated, >=1 if some backups are restored
 | 
						|
int Check_recovery(void)
 | 
						|
{
 | 
						|
  int restored_spare;
 | 
						|
  int restored_main;
 | 
						|
 | 
						|
  // First check if can write backups
 | 
						|
#if defined (__MINT__)
 | 
						|
   //TODO: enable file lock under Freemint only
 | 
						|
   return 0;
 | 
						|
#else
 | 
						|
if (Create_lock_file(Config_directory))
 | 
						|
    return -1;
 | 
						|
#endif
 | 
						|
 | 
						|
  Safety_backup_active=1;
 | 
						|
 | 
						|
  Backups_main = NULL;
 | 
						|
  Backups_spare = NULL;
 | 
						|
  For_each_file(Config_directory, Add_backup_file);
 | 
						|
 | 
						|
  // Do the processing twice: once for possible backups of the main page,
 | 
						|
  // once for possible backups of the spare.
 | 
						|
 | 
						|
  restored_spare = Process_backups(&Backups_spare);
 | 
						|
  if (restored_spare)
 | 
						|
  {
 | 
						|
    Main.offset_X=0;
 | 
						|
    Main.offset_Y=0;
 | 
						|
    Compute_limits();
 | 
						|
    Compute_paintbrush_coordinates();
 | 
						|
    if (Backups_main)
 | 
						|
      Button_Page(BUTTON_PAGE);
 | 
						|
  }
 | 
						|
  restored_main = Process_backups(&Backups_main);
 | 
						|
 | 
						|
  if (restored_main)
 | 
						|
  {
 | 
						|
    Main.offset_X=0;
 | 
						|
    Main.offset_Y=0;
 | 
						|
    Compute_limits();
 | 
						|
    Compute_paintbrush_coordinates();
 | 
						|
  }
 | 
						|
  return restored_main + restored_spare;
 | 
						|
}
 | 
						|
 | 
						|
void Rotate_safety_backups(void)
 | 
						|
{
 | 
						|
  dword now;
 | 
						|
  T_IO_Context context;
 | 
						|
  char file_name[12+1];
 | 
						|
 | 
						|
  if (!Safety_backup_active)
 | 
						|
    return;
 | 
						|
 | 
						|
  now = GFX2_GetTicks();
 | 
						|
  // It's time to save if either:
 | 
						|
  // - Many edits have taken place
 | 
						|
  // - A minimum number of edits have taken place AND a minimum time has passed
 | 
						|
  // - At least one edit was done, and a maximum time has passed
 | 
						|
  if ((Main.edits_since_safety_backup > Max_edits_for_safety_backup) ||
 | 
						|
      (Main.edits_since_safety_backup > Min_edits_for_safety_backup &&
 | 
						|
      now > Main.time_of_safety_backup + Min_interval_for_safety_backup) ||
 | 
						|
      (Main.edits_since_safety_backup > 1 &&
 | 
						|
      now > Main.time_of_safety_backup + Max_interval_for_safety_backup))
 | 
						|
  {
 | 
						|
    char * deleted_file;
 | 
						|
    size_t len = strlen(Config_directory) + strlen(BACKUP_FILE_EXTENSION) + 1 + 6 + 1;
 | 
						|
 | 
						|
    deleted_file = GFX2_malloc(len);
 | 
						|
    if (deleted_file == NULL)
 | 
						|
      return;
 | 
						|
    // Clear a previous save (rotating saves)
 | 
						|
    snprintf(deleted_file, len, "%s%c%6.6d" BACKUP_FILE_EXTENSION,
 | 
						|
      Config_directory,
 | 
						|
      Main.safety_backup_prefix,
 | 
						|
      (dword)(Main.safety_number + 1000000l - Rotation_safety_backup) % (dword)1000000l);
 | 
						|
    Remove_path(deleted_file); // no matter if fail
 | 
						|
    free(deleted_file);
 | 
						|
 | 
						|
    // Reset counters
 | 
						|
    Main.edits_since_safety_backup=0;
 | 
						|
    Main.time_of_safety_backup=now;
 | 
						|
 | 
						|
    // Create a new file name and save
 | 
						|
    sprintf(file_name, "%c%6.6d" BACKUP_FILE_EXTENSION,
 | 
						|
      Main.safety_backup_prefix,
 | 
						|
      (int)Main.safety_number);
 | 
						|
    Init_context_backup_image(&context, file_name, Config_directory);
 | 
						|
    context.Format=FORMAT_GIF;
 | 
						|
    // Provide original file data, to store as a GIF Application Extension
 | 
						|
    context.Original_file_name = strdup(Main.backups->Pages->Filename);
 | 
						|
    context.Original_file_directory = strdup(Main.backups->Pages->File_directory);
 | 
						|
 | 
						|
    Save_image(&context);
 | 
						|
    Destroy_context(&context);
 | 
						|
 | 
						|
    Main.safety_number++;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/// Remove safety backups. Need to call on normal program exit.
 | 
						|
void Delete_safety_backups(void)
 | 
						|
{
 | 
						|
  T_String_list *element;
 | 
						|
  T_String_list *next;
 | 
						|
 | 
						|
  if (!Safety_backup_active)
 | 
						|
    return;
 | 
						|
 | 
						|
  Backups_main = NULL;
 | 
						|
  Backups_spare = NULL;
 | 
						|
 | 
						|
  For_each_file(Config_directory, Add_backup_file);
 | 
						|
 | 
						|
  Change_directory(Config_directory);
 | 
						|
  for (element=Backups_main; element!=NULL; element=next)
 | 
						|
  {
 | 
						|
    next = element->Next;
 | 
						|
    if(remove(element->String))
 | 
						|
      printf("Failed to delete %s\n",element->String);
 | 
						|
    free(element->String);
 | 
						|
    free(element);
 | 
						|
  }
 | 
						|
  Backups_main = NULL;
 | 
						|
  for (element=Backups_spare; element!=NULL; element=next)
 | 
						|
  {
 | 
						|
    next = element->Next;
 | 
						|
    if(remove(element->String))
 | 
						|
      printf("Failed to delete %s\n",element->String);
 | 
						|
    free(element->String);
 | 
						|
    free(element);
 | 
						|
  }
 | 
						|
  Backups_spare = NULL;
 | 
						|
 | 
						|
  // Release lock file
 | 
						|
#if defined (__MINT__)
 | 
						|
  //TODO: release file lock under Freemint only
 | 
						|
#else
 | 
						|
  Release_lock_file(Config_directory);
 | 
						|
#endif
 | 
						|
 | 
						|
}
 |