bool AseFormat::onSave(FileOp *fop) { Sprite* sprite = fop->document->getSprite(); ASE_Header header; ASE_FrameHeader frame_header; int frame; FileHandle f(fop->filename.c_str(), "wb"); /* prepare the header */ ase_file_prepare_header(f, &header, sprite); /* write frame */ for (frame=0; frame<sprite->getTotalFrames(); frame++) { /* prepare the header */ ase_file_prepare_frame_header(f, &frame_header); /* frame duration */ frame_header.duration = sprite->getFrameDuration(frame); /* the sprite is indexed and the palette changes? (or is the first frame) */ if (sprite->getPixelFormat() == IMAGE_INDEXED && (frame == 0 || sprite->getPalette(frame-1)->countDiff(sprite->getPalette(frame), NULL, NULL) > 0)) { /* write the color chunk */ ase_file_write_color2_chunk(f, sprite->getPalette(frame)); } /* write extra chunks in the first frame */ if (frame == 0) { LayerIterator it = sprite->getFolder()->get_layer_begin(); LayerIterator end = sprite->getFolder()->get_layer_end(); /* write layer chunks */ for (; it != end; ++it) ase_file_write_layers(f, *it); } /* write cel chunks */ ase_file_write_cels(f, sprite, sprite->getFolder(), frame); /* write the frame header */ ase_file_write_frame_header(f, &frame_header); /* progress */ if (sprite->getTotalFrames() > 1) fop_progress(fop, (float)(frame+1) / (float)(sprite->getTotalFrames())); } /* write the header */ ase_file_write_header(f, &header); if (ferror(f)) { fop_error(fop, "Error writing file.\n"); return false; } else { return true; } }
// Thread to do the hard work: save the file to the disk. virtual void onJob() override { try { fop_operate(m_fop, this); } catch (const std::exception& e) { fop_error(m_fop, "Error saving file:\n%s", e.what()); } fop_done(m_fop); }
static void output_message(j_common_ptr cinfo) { char buffer[JMSG_LENGTH_MAX]; // Format the message. (*cinfo->err->format_message)(cinfo, buffer); // Put in the log file if. PRINTF("JPEG library: \"%s\"\n", buffer); // Leave the message for the application. fop_error(((struct error_mgr *)cinfo->err)->fop, "%s\n", buffer); }
// 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 FliFormat::onSave(FileOp* fop) { Sprite* sprite = fop->document->getSprite(); unsigned char cmap[768]; unsigned char omap[768]; s_fli_header fli_header; int c, times; Image *bmp, *old; Palette *pal; /* prepare fli header */ fli_header.filesize = 0; fli_header.frames = 0; fli_header.width = sprite->getWidth(); fli_header.height = sprite->getHeight(); if ((fli_header.width == 320) && (fli_header.height == 200)) fli_header.magic = HEADER_FLI; else fli_header.magic = HEADER_FLC; fli_header.depth = 8; fli_header.flags = 3; fli_header.speed = get_time_precision(sprite); fli_header.created = 0; fli_header.updated = 0; fli_header.aspect_x = 1; fli_header.aspect_y = 1; fli_header.oframe1 = fli_header.oframe2 = 0; /* open the file to write in binary mode */ FileHandle f(fop->filename.c_str(), "wb"); fseek(f, 128, SEEK_SET); /* create the bitmaps */ bmp = Image::create(IMAGE_INDEXED, sprite->getWidth(), sprite->getHeight()); old = Image::create(IMAGE_INDEXED, sprite->getWidth(), sprite->getHeight()); if ((!bmp) || (!old)) { fop_error(fop, "Not enough memory for temporary bitmaps.\n"); if (bmp) image_free(bmp); if (old) image_free(old); return false; } /* write frame by frame */ for (FrameNumber frpos(0); frpos < sprite->getTotalFrames(); ++frpos) { /* get color map */ pal = sprite->getPalette(frpos); for (c=0; c<256; c++) { cmap[3*c ] = _rgba_getr(pal->getEntry(c)); cmap[3*c+1] = _rgba_getg(pal->getEntry(c)); cmap[3*c+2] = _rgba_getb(pal->getEntry(c)); } /* render the frame in the bitmap */ image_clear(bmp, 0); layer_render(sprite->getFolder(), bmp, 0, 0, frpos); /* how many times this frame should be written to get the same time that it has in the sprite */ times = sprite->getFrameDuration(frpos) / fli_header.speed; for (c=0; c<times; c++) { /* write this frame */ if (frpos == 0 && c == 0) fli_write_frame(f, &fli_header, NULL, NULL, (unsigned char *)bmp->dat, cmap, W_ALL); else fli_write_frame(f, &fli_header, (unsigned char *)old->dat, omap, (unsigned char *)bmp->dat, cmap, W_ALL); /* 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.next()) / (float)(sprite->getTotalFrames())); } /* write the header and close the file */ fli_write_header(f, &fli_header); /* destroy the bitmaps */ image_free(bmp); image_free(old); return true; }
static Cel *ase_file_read_cel_chunk(FILE *f, Sprite *sprite, int frame, PixelFormat pixelFormat, FileOp *fop, ASE_Header *header, size_t chunk_end) { /* read chunk data */ int layer_index = fgetw(f); int x = ((short)fgetw(f)); int y = ((short)fgetw(f)); int opacity = fgetc(f); int cel_type = fgetw(f); Layer* layer; ase_file_read_padding(f, 7); layer = sprite->indexToLayer(layer_index); if (!layer) { fop_error(fop, "Frame %d didn't found layer with index %d\n", frame, layer_index); return NULL; } if (!layer->is_image()) { fop_error(fop, "Invalid .ase file (frame %d in layer %d which does not contain images\n", frame, layer_index); return NULL; } // Create the new frame. UniquePtr<Cel> cel(new Cel(frame, 0)); cel->setPosition(x, y); cel->setOpacity(opacity); switch (cel_type) { case ASE_FILE_RAW_CEL: { // Read width and height int w = fgetw(f); int h = fgetw(f); if (w > 0 && h > 0) { Image* image = Image::create(pixelFormat, w, h); // Read pixel data switch (image->getPixelFormat()) { case IMAGE_RGB: read_raw_image<RgbTraits>(f, image, fop, header); break; case IMAGE_GRAYSCALE: read_raw_image<GrayscaleTraits>(f, image, fop, header); break; case IMAGE_INDEXED: read_raw_image<IndexedTraits>(f, image, fop, header); break; } cel->setImage(sprite->getStock()->addImage(image)); } break; } case ASE_FILE_LINK_CEL: { // Read link position int link_frame = fgetw(f); Cel* link = static_cast<LayerImage*>(layer)->getCel(link_frame); if (link) { // Create a copy of the linked cel (avoid using links cel) Image* image = Image::createCopy(sprite->getStock()->getImage(link->getImage())); cel->setImage(sprite->getStock()->addImage(image)); } else { // Linked cel doesn't found return NULL; } break; } case ASE_FILE_COMPRESSED_CEL: { // Read width and height int w = fgetw(f); int h = fgetw(f); if (w > 0 && h > 0) { Image* image = Image::create(pixelFormat, w, h); // Try to read pixel data try { switch (image->getPixelFormat()) { case IMAGE_RGB: read_compressed_image<RgbTraits>(f, image, chunk_end, fop, header); break; case IMAGE_GRAYSCALE: read_compressed_image<GrayscaleTraits>(f, image, chunk_end, fop, header); break; case IMAGE_INDEXED: read_compressed_image<IndexedTraits>(f, image, chunk_end, fop, header); break; } } // OK, in case of error we can show the problem, but continue // loading more cels. catch (const std::exception& e) { fop_error(fop, e.what()); } cel->setImage(sprite->getStock()->addImage(image)); } break; } } Cel* newCel = cel.release(); static_cast<LayerImage*>(layer)->addCel(newCel); return newCel; }
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::onSave(FileOp* fop) { struct jpeg_compress_struct cinfo; struct error_mgr jerr; Image *image = fop->seq.image; FILE *file; JSAMPARRAY buffer; JDIMENSION buffer_height; SharedPtr<JpegOptions> jpeg_options = fop->seq.format_options; int c; // Open the file for write in it. file = fopen(fop->filename.c_str(), "wb"); if (!file) { fop_error(fop, "Error creating file.\n"); return false; } // Allocate and initialize JPEG compression object. jerr.fop = fop; cinfo.err = jpeg_std_error(&jerr.head); jpeg_create_compress(&cinfo); // SPECIFY data destination file. jpeg_stdio_dest(&cinfo, file); // SET parameters for compression. cinfo.image_width = image->w; cinfo.image_height = image->h; if (image->imgtype == IMAGE_GRAYSCALE) { cinfo.input_components = 1; cinfo.in_color_space = JCS_GRAYSCALE; } else { cinfo.input_components = 3; cinfo.in_color_space = JCS_RGB; } jpeg_set_defaults(&cinfo); jpeg_set_quality(&cinfo, (int)MID(0, 100.0f * jpeg_options->quality, 100), true); cinfo.dct_method = JDCT_ISLOW; cinfo.smoothing_factor = 0; // START compressor. jpeg_start_compress(&cinfo, true); // CREATE the buffer. buffer_height = 1; buffer = (JSAMPARRAY)base_malloc(sizeof(JSAMPROW) * buffer_height); if (!buffer) { fop_error(fop, "Not enough memory for the buffer.\n"); jpeg_destroy_compress(&cinfo); fclose(file); return false; } for (c=0; c<(int)buffer_height; c++) { buffer[c] = (JSAMPROW)base_malloc(sizeof(JSAMPLE) * cinfo.image_width * cinfo.num_components); if (!buffer[c]) { fop_error(fop, "Not enough memory for buffer scanlines.\n"); for (c--; c>=0; c--) base_free(buffer[c]); base_free(buffer); jpeg_destroy_compress(&cinfo); fclose(file); return false; } } // Write each scan line. while (cinfo.next_scanline < cinfo.image_height) { // RGB if (image->imgtype == IMAGE_RGB) { uint32_t* src_address; uint8_t* dst_address; int x, y; for (y=0; y<(int)buffer_height; y++) { src_address = ((uint32_t**)image->line)[cinfo.next_scanline+y]; dst_address = ((uint8_t**)buffer)[y]; for (x=0; x<image->w; x++) { c = *(src_address++); *(dst_address++) = _rgba_getr(c); *(dst_address++) = _rgba_getg(c); *(dst_address++) = _rgba_getb(c); } } } // Grayscale. else { uint16_t* src_address; uint8_t* dst_address; int x, y; for (y=0; y<(int)buffer_height; y++) { src_address = ((uint16_t**)image->line)[cinfo.next_scanline+y]; dst_address = ((uint8_t**)buffer)[y]; for (x=0; x<image->w; x++) *(dst_address++) = _graya_getv(*(src_address++)); } } jpeg_write_scanlines(&cinfo, buffer, buffer_height); fop_progress(fop, (float)(cinfo.next_scanline+1) / (float)(cinfo.image_height)); } // Destroy all data. for (c=0; c<(int)buffer_height; c++) base_free(buffer[c]); base_free(buffer); // Finish compression. jpeg_finish_compress(&cinfo); // Release JPEG compression object. jpeg_destroy_compress(&cinfo); // We can close the output file. fclose(file); // All fine. return true; }