diff --git a/src/fileformats.c b/src/fileformats.c index 7d33c822..0770c505 100644 --- a/src/fileformats.c +++ b/src/fileformats.c @@ -6236,7 +6236,7 @@ static int PNG_read_unknown_chunk(png_structp ptr, png_unknown_chunkp chunk) struct PNG_memory_buffer { - const char * buffer; + char * buffer; unsigned long offset; unsigned long size; }; @@ -6294,7 +6294,7 @@ void Load_PNG_Sub(T_IO_Context * context, FILE * file, const char * memory_buffe png_init_io(png_ptr, file); else { - buffer.buffer = memory_buffer; + buffer.buffer = (char *)memory_buffer; buffer.offset = 8; // skip header buffer.size = memory_buffer_size; png_set_read_fn(png_ptr, &buffer, PNG_memory_read); @@ -6623,165 +6623,184 @@ void Load_PNG(T_IO_Context * context) File_error=1; } -/// Save a PNG file -void Save_PNG(T_IO_Context * context) + +static void PNG_memory_write(png_structp png_ptr, png_bytep p, png_size_t count) { - FILE *file; + struct PNG_memory_buffer * buffer = (struct PNG_memory_buffer *)png_get_io_ptr(png_ptr); + GFX2_Log(GFX2_DEBUG, "PNG_memory_write(%p, %p, %u) (io_ptr=%p)\n", png_ptr, p, count, buffer); + if (buffer->size < buffer->offset + count) + { + char * tmp = realloc(buffer->buffer, buffer->offset + count + 1024); + if (tmp == NULL) + { + GFX2_Log(GFX2_ERROR, "PNG_memory_write() Failed to allocate %u bytes of memory\n", buffer->offset + count + 1024); + File_error = 1; + return; + } + buffer->buffer = tmp; + buffer->size = buffer->offset + count + 1024; + } + memcpy(buffer->buffer + buffer->offset, p, count); + buffer->offset += count; +} + +static void PNG_memory_flush(png_structp png_ptr) +{ + struct PNG_memory_buffer * buffer = (struct PNG_memory_buffer *)png_get_io_ptr(png_ptr); + GFX2_Log(GFX2_DEBUG, "PNG_memory_flush(%p) (io_ptr=%p)\n", png_ptr, buffer); +} + +void Save_PNG_Sub(T_IO_Context * context, FILE * file, char * * buffer, unsigned long * buffer_size) +{ + static png_bytep * Row_pointers = NULL; int y; byte * pixel_ptr; png_structp png_ptr; png_infop info_ptr; png_unknown_chunk crng_chunk; byte cycle_data[16*6]; // Storage for color-cycling data, referenced by crng_chunk - static png_bytep * Row_pointers; - - File_error=0; - Row_pointers = NULL; - - // Ouverture du fichier - if ((file=Open_file_write(context))) - { - setvbuf(file, NULL, _IOFBF, 64*1024); - - /* initialisation */ - if ((png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL)) + struct PNG_memory_buffer memory_buffer; + + /* initialisation */ + if ((png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL)) && (info_ptr = png_create_info_struct(png_ptr))) + { + if (!setjmp(png_jmpbuf(png_ptr))) { - - if (!setjmp(png_jmpbuf(png_ptr))) - { + if (file != NULL) png_init_io(png_ptr, file); + else + { + memset(&memory_buffer, 0, sizeof(memory_buffer)); + png_set_write_fn(png_ptr, &memory_buffer, PNG_memory_write, PNG_memory_flush); + } - /* en-tete */ - if (!setjmp(png_jmpbuf(png_ptr))) - { - png_set_IHDR(png_ptr, info_ptr, context->Width, context->Height, + /* en-tete */ + if (!setjmp(png_jmpbuf(png_ptr))) + { + png_set_IHDR(png_ptr, info_ptr, context->Width, context->Height, 8, PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); - png_set_PLTE(png_ptr, info_ptr, (png_colorp)context->Palette, 256); + png_set_PLTE(png_ptr, info_ptr, (png_colorp)context->Palette, 256); + { + // Commentaires texte PNG + // Cette partie est optionnelle + png_text text_ptr[2] = { +#ifdef PNG_iTXt_SUPPORTED + {-1, "Software", "Grafx2", 6, 0, NULL, NULL}, + {-1, "Title", NULL, 0, 0, NULL, NULL} +#else + {-1, "Software", "Grafx2", 6}, + {-1, "Title", NULL, 0} +#endif + }; + int nb_text_chunks=1; + if (context->Comment[0]) { - // Commentaires texte PNG - // Cette partie est optionnelle - #ifdef PNG_iTXt_SUPPORTED - png_text text_ptr[2] = { - {-1, "Software", "Grafx2", 6, 0, NULL, NULL}, - {-1, "Title", NULL, 0, 0, NULL, NULL} - #else - png_text text_ptr[2] = { - {-1, "Software", "Grafx2", 6}, - {-1, "Title", NULL, 0} - #endif - }; - int nb_text_chunks=1; - if (context->Comment[0]) - { - text_ptr[1].text=context->Comment; - text_ptr[1].text_length=strlen(context->Comment); - nb_text_chunks=2; - } - png_set_text(png_ptr, info_ptr, text_ptr, nb_text_chunks); + text_ptr[1].text=context->Comment; + text_ptr[1].text_length=strlen(context->Comment); + nb_text_chunks=2; } - if (context->Background_transparent) - { - // Transparency - byte opacity[256]; - // Need to fill a segment with '255', up to the transparent color - // which will have a 0. This piece of data (1 to 256 bytes) - // will be stored in the file. - memset(opacity, 255,context->Transparent_color); - opacity[context->Transparent_color]=0; - png_set_tRNS(png_ptr, info_ptr, opacity, (int)1 + context->Transparent_color,0); - } - // if using PNG_RESOLUTION_METER, unit is in dot per meter. - // 72 DPI = 2835, 600 DPI = 23622 - // with PNG_RESOLUTION_UNKNOWN, it is arbitrary - switch(Pixel_ratio) - { - case PIXEL_WIDE: - case PIXEL_WIDE2: - png_set_pHYs(png_ptr, info_ptr, 1, 2, PNG_RESOLUTION_UNKNOWN); - break; - case PIXEL_TALL: - case PIXEL_TALL2: - png_set_pHYs(png_ptr, info_ptr, 2, 1, PNG_RESOLUTION_UNKNOWN); - break; - case PIXEL_TALL3: - png_set_pHYs(png_ptr, info_ptr, 4, 3, PNG_RESOLUTION_UNKNOWN); - break; - default: - break; - } - // Write cycling colors - if (context->Color_cycles) - { - // Save a chunk called 'crNg' - // The case is selected by the following rules from PNG standard: - // char 1: non-mandatory = lowercase - // char 2: private (not standard) = lowercase - // char 3: reserved = always uppercase - // char 4: can be copied by editors = lowercase + png_set_text(png_ptr, info_ptr, text_ptr, nb_text_chunks); + } + if (context->Background_transparent) + { + // Transparency + byte opacity[256]; + // Need to fill a segment with '255', up to the transparent color + // which will have a 0. This piece of data (1 to 256 bytes) + // will be stored in the file. + memset(opacity, 255,context->Transparent_color); + opacity[context->Transparent_color]=0; + png_set_tRNS(png_ptr, info_ptr, opacity, (int)1 + context->Transparent_color,0); + } + // if using PNG_RESOLUTION_METER, unit is in dot per meter. + // 72 DPI = 2835, 600 DPI = 23622 + // with PNG_RESOLUTION_UNKNOWN, it is arbitrary + switch(Pixel_ratio) + { + case PIXEL_WIDE: + case PIXEL_WIDE2: + png_set_pHYs(png_ptr, info_ptr, 1, 2, PNG_RESOLUTION_UNKNOWN); + break; + case PIXEL_TALL: + case PIXEL_TALL2: + png_set_pHYs(png_ptr, info_ptr, 2, 1, PNG_RESOLUTION_UNKNOWN); + break; + case PIXEL_TALL3: + png_set_pHYs(png_ptr, info_ptr, 4, 3, PNG_RESOLUTION_UNKNOWN); + break; + default: + break; + } + // Write cycling colors + if (context->Color_cycles) + { + // Save a chunk called 'crNg' + // The case is selected by the following rules from PNG standard: + // char 1: non-mandatory = lowercase + // char 2: private (not standard) = lowercase + // char 3: reserved = always uppercase + // char 4: can be copied by editors = lowercase - // First, turn our nice structure into byte array - // (just to avoid padding in structures) + // First, turn our nice structure into byte array + // (just to avoid padding in structures) - byte *chunk_ptr = cycle_data; - int i; + byte *chunk_ptr = cycle_data; + int i; - for (i=0; iColor_cycles; i++) - { - word flags=0; - flags|= context->Cycle_range[i].Speed?1:0; // Cycling or not - flags|= context->Cycle_range[i].Inverse?2:0; // Inverted - - // Big end of Rate - *(chunk_ptr++) = (context->Cycle_range[i].Speed*78) >> 8; - // Low end of Rate - *(chunk_ptr++) = (context->Cycle_range[i].Speed*78) & 0xFF; - - // Big end of Flags - *(chunk_ptr++) = (flags) >> 8; - // Low end of Flags - *(chunk_ptr++) = (flags) & 0xFF; - - // Min color - *(chunk_ptr++) = context->Cycle_range[i].Start; - // Max color - *(chunk_ptr++) = context->Cycle_range[i].End; - } + for (i=0; iColor_cycles; i++) + { + word flags=0; + flags|= context->Cycle_range[i].Speed?1:0; // Cycling or not + flags|= context->Cycle_range[i].Inverse?2:0; // Inverted - // Build one unknown_chuck structure - memcpy(crng_chunk.name, "crNg",5); - crng_chunk.data=cycle_data; - crng_chunk.size=context->Color_cycles*6; - crng_chunk.location=PNG_HAVE_PLTE; - - // Give it to libpng - png_set_unknown_chunks(png_ptr, info_ptr, &crng_chunk, 1); - // libpng seems to ignore the location I provided earlier. - png_set_unknown_chunk_location(png_ptr, info_ptr, 0, PNG_HAVE_PLTE); + // Big end of Rate + *(chunk_ptr++) = (context->Cycle_range[i].Speed*78) >> 8; + // Low end of Rate + *(chunk_ptr++) = (context->Cycle_range[i].Speed*78) & 0xFF; + + // Big end of Flags + *(chunk_ptr++) = (flags) >> 8; + // Low end of Flags + *(chunk_ptr++) = (flags) & 0xFF; + + // Min color + *(chunk_ptr++) = context->Cycle_range[i].Start; + // Max color + *(chunk_ptr++) = context->Cycle_range[i].End; } - - - png_write_info(png_ptr, info_ptr); - /* ecriture des pixels de l'image */ - Row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * context->Height); - pixel_ptr = context->Target_address; - for (y=0; yHeight; y++) - Row_pointers[y] = (png_byte*)(pixel_ptr+y*context->Pitch); + // Build one unknown_chuck structure + memcpy(crng_chunk.name, "crNg",5); + crng_chunk.data=cycle_data; + crng_chunk.size=context->Color_cycles*6; + crng_chunk.location=PNG_HAVE_PLTE; + // Give it to libpng + png_set_unknown_chunks(png_ptr, info_ptr, &crng_chunk, 1); + // libpng seems to ignore the location I provided earlier. + png_set_unknown_chunk_location(png_ptr, info_ptr, 0, PNG_HAVE_PLTE); + } + + + png_write_info(png_ptr, info_ptr); + + /* ecriture des pixels de l'image */ + Row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * context->Height); + pixel_ptr = context->Target_address; + for (y=0; yHeight; y++) + Row_pointers[y] = (png_byte*)(pixel_ptr+y*context->Pitch); + + if (!setjmp(png_jmpbuf(png_ptr))) + { + png_write_image(png_ptr, Row_pointers); + + /* cloture png */ if (!setjmp(png_jmpbuf(png_ptr))) { - png_write_image(png_ptr, Row_pointers); - - /* cloture png */ - if (!setjmp(png_jmpbuf(png_ptr))) - { - png_write_end(png_ptr, NULL); - } - else - File_error=1; + png_write_end(png_ptr, NULL); } else File_error=1; @@ -6790,27 +6809,44 @@ void Save_PNG(T_IO_Context * context) File_error=1; } else - { File_error=1; - } - png_destroy_write_struct(&png_ptr, &info_ptr); } else + { File_error=1; - // fermeture du fichier - fclose(file); + } + png_destroy_write_struct(&png_ptr, &info_ptr); } + else + File_error=1; - // S'il y a eu une erreur de sauvegarde, on ne va tout de même pas laisser - // ce fichier pourri trainait... Ca fait pas propre. - if (File_error) - Remove_file(context); - if (Row_pointers) - { free(Row_pointers); - Row_pointers=NULL; + if (memory_buffer.buffer) + { + *buffer = memory_buffer.buffer; + *buffer_size = memory_buffer.offset; } } + +/// Save a PNG file +void Save_PNG(T_IO_Context * context) +{ + FILE *file; + + File_error = 0; + + file = Open_file_write(context); + if (file != NULL) + { + Save_PNG_Sub(context, file, NULL, NULL); + fclose(file); + // remove the file if there was an error + if (File_error) + Remove_file(context); + } + else + File_error = 1; +} #endif // __no_pnglib__ diff --git a/src/fileformats.h b/src/fileformats.h index 87c11ca4..e9c67107 100644 --- a/src/fileformats.h +++ b/src/fileformats.h @@ -141,6 +141,7 @@ void Test_PNG(T_IO_Context *, FILE *); void Load_PNG(T_IO_Context *); void Save_PNG(T_IO_Context *); void Load_PNG_Sub(T_IO_Context * context, FILE * file, const char * memory_buffer, unsigned long memory_buffer_size); +void Save_PNG_Sub(T_IO_Context * context, FILE * file, char * * buffer, unsigned long * buffer_size); #endif // -- INFO (Amiga ICONS) ---------------------------------------------------- diff --git a/src/input.c b/src/input.c index a2d5c835..56f6549c 100644 --- a/src/input.c +++ b/src/input.c @@ -38,6 +38,7 @@ #include #include #include +#include #endif #include "gfx2log.h" @@ -522,6 +523,60 @@ static int Handle_SelectionNotify(const XSelectionEvent* xselection) } return user_feedback_required; } + +static void Handle_SelectionRequest(const XSelectionRequestEvent* xselectionrequest) +{ + XSelectionEvent xselection; + char * target_name; + char * property_name; + Atom png; +#if defined(SDL_VIDEO_DRIVER_X11) + Display * X11_display; + Window X11_window; + + if (!GFX2_Get_X11_Display_Window(&X11_display, &X11_window)) + { + GFX2_Log(GFX2_ERROR, "Failed to get X11 display and window\n"); + return; + } +#endif + + png = XInternAtom(X11_display, "image/png", False); + + target_name = XGetAtomName(X11_display, xselectionrequest->target); + property_name = XGetAtomName(X11_display, xselectionrequest->property); + GFX2_Log(GFX2_DEBUG, "Handle_SelectionRequest target=%s property=%s\n", target_name, property_name); + XFree(target_name); + XFree(property_name); + + xselection.type = SelectionNotify; + xselection.requestor = xselectionrequest->requestor; + xselection.selection = xselectionrequest->selection; + xselection.target = xselectionrequest->target; + xselection.property = xselectionrequest->property; + xselection.time = xselectionrequest->time; + + if (xselectionrequest->target == XInternAtom(X11_display, "TARGETS", False)) + { + Atom targets[1]; + targets[0] = png; + XChangeProperty(X11_display, xselectionrequest->requestor, xselectionrequest->property, + XA_ATOM, 32, PropModeReplace, + (unsigned char *)targets, 1); + } + else if (xselectionrequest->target == png) + { + XChangeProperty(X11_display, xselectionrequest->requestor, xselectionrequest->property, + png, 8, PropModeReplace, + (unsigned char *)X11_clipboard, X11_clipboard_size); + } + else + { + xselection.property = None; // refuse + } + + XSendEvent(X11_display, xselectionrequest->requestor, True, NoEventMask, (XEvent *)&xselection); +} #endif #if defined(USE_SDL) @@ -1351,6 +1406,18 @@ int Get_input(int sleep_time) if (Handle_SelectionNotify(&(xevent.xselection))) user_feedback_required = 1; break; + case SelectionRequest: + Handle_SelectionRequest(&(xevent.xselectionrequest)); + break; + case SelectionClear: + GFX2_Log(GFX2_DEBUG, "X11 SelectionClear\n"); + if (X11_clipboard) + { + free(X11_clipboard); + X11_clipboard_size = 0; + } + SDL_EventState(SDL_SYSWMEVENT, SDL_DISABLE); + break; case ButtonPress: case ButtonRelease: case MotionNotify: @@ -1687,6 +1754,23 @@ int Get_input(int sleep_time) if (Handle_SelectionNotify(&event.xselection)) user_feedback_required = 1; break; + case SelectionClear: + GFX2_Log(GFX2_DEBUG, "X11 SelectionClear\n"); + if (X11_clipboard) + { + free(X11_clipboard); + X11_clipboard_size = 0; + } + break; + case SelectionRequest: + Handle_SelectionRequest(&event.xselectionrequest); + break; + case ReparentNotify: + GFX2_Log(GFX2_DEBUG, "X11 ReparentNotify\n"); + break; + case MapNotify: + GFX2_Log(GFX2_DEBUG, "X11 MapNotify\n"); + break; default: GFX2_Log(GFX2_INFO, "X11 event.type = %d not handled\n", event.type); } diff --git a/src/loadsave.c b/src/loadsave.c index 2363e985..8037ad73 100644 --- a/src/loadsave.c +++ b/src/loadsave.c @@ -1566,6 +1566,46 @@ static void Save_ClipBoard_Image(T_IO_Context * context) } } CloseClipboard(); +#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 GFX2_Log(GFX2_ERROR, "Save_ClipBoard_Image() not implemented on this platform yet\n"); File_error = 1;