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; 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(open_file_with_exception(fop->filename, "wb")); fseek(f, 128, SEEK_SET); // Create the bitmaps base::UniquePtr<Image> bmp(Image::create(IMAGE_INDEXED, sprite->getWidth(), sprite->getHeight())); base::UniquePtr<Image> old(Image::create(IMAGE_INDEXED, sprite->getWidth(), sprite->getHeight())); // 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 */ clear_image(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->getPixelAddress(0, 0), cmap, W_ALL); else fli_write_frame(f, &fli_header, (unsigned char *)old->getPixelAddress(0, 0), omap, (unsigned char *)bmp->getPixelAddress(0, 0), cmap, W_ALL); /* update the old image and color-map to the new ones to compare later */ copy_image(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); return true; }
static void read_compressed_image(FILE* f, Image* image, size_t chunk_end, FileOp* fop, ASE_Header* header) { PixelIO<ImageTraits> pixel_io; z_stream zstream; int y, err; zstream.zalloc = (alloc_func)0; zstream.zfree = (free_func)0; zstream.opaque = (voidpf)0; err = inflateInit(&zstream); if (err != Z_OK) throw base::Exception("ZLib error %d in inflateInit().", err); std::vector<uint8_t> scanline(ImageTraits::scanline_size(image->w)); std::vector<uint8_t> uncompressed(image->h * ImageTraits::scanline_size(image->w)); std::vector<uint8_t> compressed(4096); int uncompressed_offset = 0; while (true) { size_t input_bytes; if (ftell(f)+compressed.size() > chunk_end) { input_bytes = chunk_end - ftell(f); // Remaining bytes ASSERT(input_bytes < compressed.size()); if (input_bytes == 0) break; // Done, we consumed all chunk } else input_bytes = compressed.size(); fread(&compressed[0], 1, input_bytes, f); zstream.next_in = (Bytef*)&compressed[0]; zstream.avail_in = input_bytes; do { zstream.next_out = (Bytef*)&scanline[0]; zstream.avail_out = scanline.size(); err = inflate(&zstream, Z_NO_FLUSH); if (err != Z_OK && err != Z_STREAM_END) throw base::Exception("ZLib error %d in inflate().", err); size_t input_bytes = scanline.size() - zstream.avail_out; if (input_bytes > 0) { if (uncompressed_offset+input_bytes > uncompressed.size()) throw base::Exception("Bad compressed image."); std::copy(scanline.begin(), scanline.begin()+input_bytes, uncompressed.begin()+uncompressed_offset); uncompressed_offset += input_bytes; } } while (zstream.avail_out == 0); fop_progress(fop, (float)ftell(f) / (float)header->size); } uncompressed_offset = 0; for (y=0; y<image->h; y++) { typename ImageTraits::address_t address = image_address_fast<ImageTraits>(image, 0, y); pixel_io.read_scanline(address, image->w, &uncompressed[uncompressed_offset]); uncompressed_offset += ImageTraits::scanline_size(image->w); } err = inflateEnd(&zstream); if (err != Z_OK) throw base::Exception("ZLib error %d in inflateEnd().", err); }
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; }
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; }