static void save_document_in_background(Context* context, Document* document, bool mark_as_saved) { base::UniquePtr<FileOp> fop(fop_to_save_document(context, document)); if (!fop) return; SaveFileJob job(fop); job.showProgressWindow(); if (fop->has_error()) { Console console; console.printf(fop->error.c_str()); // We don't know if the file was saved correctly or not. So mark // it as it should be saved again. document->impossibleToBackToSavedState(); } // If the job was cancelled, mark the document as modified. else if (fop_is_stop(fop)) { document->impossibleToBackToSavedState(); } else { App::instance()->getRecentFiles()->addRecentFile(document->filename().c_str()); if (mark_as_saved) document->markAsSaved(); StatusBar::instance() ->setStatusText(2000, "File %s, saved.", document->name().c_str()); } }
void OpenFileCommand::onExecute(Context* context) { Console console; // interactive if (context->isUiAvailable() && m_filename.empty()) { std::string exts = get_readable_extensions(); // Add backslash as show_file_selector() expected a filename as // initial path (and the file part is removed from the path). if (!m_folder.empty() && !base::is_path_separator(m_folder[m_folder.size()-1])) m_folder.push_back(base::path_separator); m_filename = app::show_file_selector("Open", m_folder, exts, FileSelectorType::Open); } if (!m_filename.empty()) { base::UniquePtr<FileOp> fop(fop_to_load_document(context, m_filename.c_str(), FILE_LOAD_SEQUENCE_ASK)); bool unrecent = false; if (fop) { if (fop->has_error()) { console.printf(fop->error.c_str()); unrecent = true; } else { OpenFileJob task(fop); task.showProgressWindow(); // Post-load processing, it is called from the GUI because may require user intervention. fop_post_load(fop); // Show any error if (fop->has_error()) console.printf(fop->error.c_str()); Document* document = fop->document; if (document) { App::instance()->getRecentFiles()->addRecentFile(fop->filename.c_str()); document->setContext(context); } else if (!fop_is_stop(fop)) unrecent = true; } // The file was not found or was loaded loaded with errors, // so we can remove it from the recent-file list if (unrecent) { App::instance()->getRecentFiles()->removeRecentFile(m_filename.c_str()); } } else { // Do nothing (the user cancelled or something like that) } } }
// Thread to do the hard work: load the file from the disk. virtual void onJob() override { try { fop_operate(m_fop, this); } catch (const std::exception& e) { fop_error(m_fop, "Error loading file:\n%s", e.what()); } if (fop_is_stop(m_fop) && m_fop->document) { delete m_fop->document; m_fop->document = NULL; } fop_done(m_fop); }
bool FliFormat::onLoad(FileOp* fop) { #define SETPAL() \ do { \ for (c=0; c<256; c++) { \ pal->setEntry(c, _rgba(cmap[c*3], \ cmap[c*3+1], \ cmap[c*3+2], 255)); \ } \ pal->setFrame(frpos_out); \ sprite->setPalette(pal, true); \ } while (0) unsigned char cmap[768]; unsigned char omap[768]; s_fli_header fli_header; Image *bmp, *old, *image; Sprite *sprite; LayerImage *layer; Palette *pal; int c, w, h; FrameNumber frpos_in; FrameNumber frpos_out; int index = 0; Cel *cel; /* open the file to read in binary mode */ FileHandle f(fop->filename.c_str(), "rb"); fli_read_header(f, &fli_header); fseek(f, 128, SEEK_SET); if (fli_header.magic == NO_HEADER) { fop_error(fop, "The file doesn't have a FLIC header\n"); return false; } /* size by frame */ w = fli_header.width; h = fli_header.height; /* create the bitmaps */ bmp = Image::create(IMAGE_INDEXED, w, h); old = Image::create(IMAGE_INDEXED, w, h); pal = new Palette(FrameNumber(0), 256); if (!bmp || !old || !pal) { fop_error(fop, "Not enough memory.\n"); if (bmp) image_free(bmp); if (old) image_free(old); if (pal) delete pal; return false; } // Create the image sprite = new Sprite(IMAGE_INDEXED, w, h, 256); layer = new LayerImage(sprite); sprite->getFolder()->addLayer(layer); layer->configureAsBackground(); // Set frames and speed sprite->setTotalFrames(FrameNumber(fli_header.frames)); sprite->setDurationForAllFrames(fli_header.speed); /* write frame by frame */ for (frpos_in = frpos_out = FrameNumber(0); frpos_in < sprite->getTotalFrames(); ++frpos_in) { /* read the frame */ fli_read_frame(f, &fli_header, (unsigned char *)old->dat, omap, (unsigned char *)bmp->dat, cmap); /* first frame, or the frames changes, or the palette changes */ if ((frpos_in == 0) || (image_count_diff(old, bmp)) #ifndef USE_LINK /* TODO this should be configurable through a check-box */ || (memcmp(omap, cmap, 768) != 0) #endif ) { /* the image changes? */ if (frpos_in != 0) ++frpos_out; /* add the new frame */ image = Image::createCopy(bmp); if (!image) { fop_error(fop, "Not enough memory\n"); break; } index = sprite->getStock()->addImage(image); if (index < 0) { image_free(image); fop_error(fop, "Not enough memory\n"); break; } cel = new Cel(frpos_out, index); layer->addCel(cel); /* first frame or the palette changes */ if ((frpos_in == 0) || (memcmp(omap, cmap, 768) != 0)) SETPAL(); } #ifdef USE_LINK /* the palette changes */ else if (memcmp(omap, cmap, 768) != 0) { ++frpos_out; SETPAL(); // Add link cel = new Cel(frpos_out, index); layer_add_cel(layer, cel); } #endif // The palette and the image don't change: add duration to the last added frame else { sprite->setFrameDuration(frpos_out, sprite->getFrameDuration(frpos_out)+fli_header.speed); } /* update the old image and color-map to the new ones to compare later */ image_copy(old, bmp, 0, 0); memcpy(omap, cmap, 768); /* update progress */ fop_progress(fop, (float)(frpos_in+1) / (float)(sprite->getTotalFrames())); if (fop_is_stop(fop)) break; /* just one frame? */ if (fop->oneframe) break; } // Update number of frames sprite->setTotalFrames(frpos_out.next()); // Destroy the bitmaps image_free(bmp); image_free(old); delete pal; fop->document = new Document(sprite); return true; }
bool AseFormat::onLoad(FileOp *fop) { Sprite *sprite = NULL; ASE_Header header; ASE_FrameHeader frame_header; int current_level; int frame_pos; int chunk_pos; int chunk_size; int chunk_type; int c, frame; FileHandle f(fop->filename.c_str(), "rb"); if (!f) return false; if (!ase_file_read_header(f, &header)) { fop_error(fop, "Error reading header\n"); return false; } // Create the new sprite sprite = new Sprite(header.depth == 32 ? IMAGE_RGB: header.depth == 16 ? IMAGE_GRAYSCALE: IMAGE_INDEXED, header.width, header.height, header.ncolors); if (!sprite) { fop_error(fop, "Error creating sprite with file spec\n"); return false; } // Set frames and speed sprite->setTotalFrames(header.frames); sprite->setDurationForAllFrames(header.speed); // Set transparent entry sprite->setTransparentColor(header.transparent_index); // Prepare variables for layer chunks Layer* last_layer = sprite->getFolder(); current_level = -1; /* read frame by frame to end-of-file */ for (frame=0; frame<sprite->getTotalFrames(); frame++) { /* start frame position */ frame_pos = ftell(f); fop_progress(fop, (float)frame_pos / (float)header.size); /* read frame header */ ase_file_read_frame_header(f, &frame_header); /* correct frame type */ if (frame_header.magic == ASE_FILE_FRAME_MAGIC) { /* use frame-duration field? */ if (frame_header.duration > 0) sprite->setFrameDuration(frame, frame_header.duration); /* read chunks */ for (c=0; c<frame_header.chunks; c++) { /* start chunk position */ chunk_pos = ftell(f); fop_progress(fop, (float)chunk_pos / (float)header.size); /* read chunk information */ chunk_size = fgetl(f); chunk_type = fgetw(f); switch (chunk_type) { /* only for 8 bpp images */ case ASE_FILE_CHUNK_FLI_COLOR: case ASE_FILE_CHUNK_FLI_COLOR2: /* fop_error(fop, "Color chunk\n"); */ if (sprite->getPixelFormat() == IMAGE_INDEXED) { Palette *prev_pal = sprite->getPalette(frame); Palette *pal = chunk_type == ASE_FILE_CHUNK_FLI_COLOR ? ase_file_read_color_chunk(f, sprite, frame): ase_file_read_color2_chunk(f, sprite, frame); if (prev_pal->countDiff(pal, NULL, NULL) > 0) { sprite->setPalette(pal, true); } delete pal; } else fop_error(fop, "Warning: was found a color chunk in non-8bpp file\n"); break; case ASE_FILE_CHUNK_LAYER: { /* fop_error(fop, "Layer chunk\n"); */ ase_file_read_layer_chunk(f, sprite, &last_layer, ¤t_level); break; } case ASE_FILE_CHUNK_CEL: { /* fop_error(fop, "Cel chunk\n"); */ ase_file_read_cel_chunk(f, sprite, frame, sprite->getPixelFormat(), fop, &header, chunk_pos+chunk_size); break; } case ASE_FILE_CHUNK_MASK: { Mask *mask; /* fop_error(fop, "Mask chunk\n"); */ mask = ase_file_read_mask_chunk(f); if (mask) delete mask; // TODO add the mask in some place? else fop_error(fop, "Warning: error loading a mask chunk\n"); break; } case ASE_FILE_CHUNK_PATH: /* fop_error(fop, "Path chunk\n"); */ break; default: fop_error(fop, "Warning: Unsupported chunk type %d (skipping)\n", chunk_type); break; } /* skip chunk size */ fseek(f, chunk_pos+chunk_size, SEEK_SET); } } /* skip frame size */ fseek(f, frame_pos+frame_header.size, SEEK_SET); /* just one frame? */ if (fop->oneframe) break; if (fop_is_stop(fop)) break; } fop->document = new Document(sprite); if (ferror(f)) { fop_error(fop, "Error reading file.\n"); return false; } else { return true; } }
bool JpegFormat::onLoad(FileOp* fop) { struct jpeg_decompress_struct cinfo; struct error_mgr jerr; Image *image; FILE *file; JDIMENSION num_scanlines; JSAMPARRAY buffer; JDIMENSION buffer_height; int c; file = fopen(fop->filename.c_str(), "rb"); if (!file) return false; // Initialize the JPEG decompression object with error handling. jerr.fop = fop; cinfo.err = jpeg_std_error(&jerr.head); jerr.head.error_exit = error_exit; jerr.head.output_message = output_message; // Establish the setjmp return context for error_exit to use. if (setjmp(jerr.setjmp_buffer)) { jpeg_destroy_decompress(&cinfo); fclose(file); return false; } jpeg_create_decompress(&cinfo); // Specify data source for decompression. jpeg_stdio_src(&cinfo, file); // Read file header, set default decompression parameters. jpeg_read_header(&cinfo, true); if (cinfo.jpeg_color_space == JCS_GRAYSCALE) cinfo.out_color_space = JCS_GRAYSCALE; else cinfo.out_color_space = JCS_RGB; // Start decompressor. jpeg_start_decompress(&cinfo); // Create the image. image = fop_sequence_image(fop, (cinfo.out_color_space == JCS_RGB ? IMAGE_RGB: IMAGE_GRAYSCALE), cinfo.output_width, cinfo.output_height); if (!image) { jpeg_destroy_decompress(&cinfo); fclose(file); return false; } // Create the buffer. buffer_height = cinfo.rec_outbuf_height; buffer = (JSAMPARRAY)base_malloc(sizeof(JSAMPROW) * buffer_height); if (!buffer) { jpeg_destroy_decompress(&cinfo); fclose(file); return false; } for (c=0; c<(int)buffer_height; c++) { buffer[c] = (JSAMPROW)base_malloc(sizeof(JSAMPLE) * cinfo.output_width * cinfo.output_components); if (!buffer[c]) { for (c--; c>=0; c--) base_free(buffer[c]); base_free(buffer); jpeg_destroy_decompress(&cinfo); fclose(file); return false; } } // Generate a grayscale palette if is necessary. if (image->imgtype == IMAGE_GRAYSCALE) for (c=0; c<256; c++) fop_sequence_set_color(fop, c, c, c, c); // Read each scan line. while (cinfo.output_scanline < cinfo.output_height) { // TODO /* if (plugin_want_close()) */ /* break; */ num_scanlines = jpeg_read_scanlines(&cinfo, buffer, buffer_height); /* RGB */ if (image->imgtype == IMAGE_RGB) { uint8_t* src_address; uint32_t* dst_address; int x, y, r, g, b; for (y=0; y<(int)num_scanlines; y++) { src_address = ((uint8_t**)buffer)[y]; dst_address = ((uint32_t**)image->line)[cinfo.output_scanline-1+y]; for (x=0; x<image->w; x++) { r = *(src_address++); g = *(src_address++); b = *(src_address++); *(dst_address++) = _rgba(r, g, b, 255); } } } /* Grayscale */ else { uint8_t* src_address; uint16_t* dst_address; int x, y; for (y=0; y<(int)num_scanlines; y++) { src_address = ((uint8_t**)buffer)[y]; dst_address = ((uint16_t**)image->line)[cinfo.output_scanline-1+y]; for (x=0; x<image->w; x++) *(dst_address++) = _graya(*(src_address++), 255); } } fop_progress(fop, (float)(cinfo.output_scanline+1) / (float)(cinfo.output_height)); if (fop_is_stop(fop)) break; } /* destroy all data */ for (c=0; c<(int)buffer_height; c++) base_free(buffer[c]); base_free(buffer); jpeg_finish_decompress(&cinfo); jpeg_destroy_decompress(&cinfo); fclose(file); return true; }