bool FliFormat::onLoad(FileOp* fop) { // Open the file to read in binary mode FileHandle handle(open_file_with_exception(fop->filename, "rb")); FILE* f = handle.get(); flic::StdioFileInterface finterface(f); flic::Decoder decoder(&finterface); flic::Header header; if (!decoder.readHeader(header)) { fop_error(fop, "The file doesn't have a FLIC header\n"); return false; } // Size by frame int w = header.width; int h = header.height; // Create a temporal bitmap ImageRef bmp(Image::create(IMAGE_INDEXED, w, h)); Palette pal(0, 1); Cel* prevCel = nullptr; // Create the sprite Sprite* sprite = new Sprite(IMAGE_INDEXED, w, h, 256); LayerImage* layer = new LayerImage(sprite); sprite->folder()->addLayer(layer); layer->configureAsBackground(); // Set frames and speed sprite->setTotalFrames(frame_t(header.frames)); sprite->setDurationForAllFrames(header.speed); flic::Frame fliFrame; flic::Colormap oldFliColormap; fliFrame.pixels = bmp->getPixelAddress(0, 0); fliFrame.rowstride = IndexedTraits::getRowStrideBytes(bmp->width()); frame_t frame_out = 0; for (frame_t frame_in=0; frame_in<sprite->totalFrames(); ++frame_in) { // Read the frame if (!decoder.readFrame(fliFrame)) { fop_error(fop, "Error reading frame %d\n", frame_in); continue; } // Palette change bool palChange = false; if (frame_out == 0 || oldFliColormap != fliFrame.colormap) { oldFliColormap = fliFrame.colormap; pal.resize(fliFrame.colormap.size()); for (int c=0; c<int(fliFrame.colormap.size()); c++) { pal.setEntry(c, rgba(fliFrame.colormap[c].r, fliFrame.colormap[c].g, fliFrame.colormap[c].b, 255)); } pal.setFrame(frame_out); sprite->setPalette(&pal, true); palChange = true; } // First frame, or the frame changes if (!prevCel || (count_diff_between_images(prevCel->image(), bmp.get()))) { // Add the new frame ImageRef image(Image::createCopy(bmp.get())); Cel* cel = new Cel(frame_out, image); layer->addCel(cel); prevCel = cel; ++frame_out; } else if (palChange) { Cel* cel = Cel::createLink(prevCel); cel->setFrame(frame_out); layer->addCel(cel); ++frame_out; } // The palette and the image don't change: add duration to the last added frame else { sprite->setFrameDuration( frame_out-1, sprite->frameDuration(frame_out-1) + header.speed); } if (header.frames > 0) fop_progress(fop, (float)(frame_in+1) / (float)(header.frames)); if (fop_is_stop(fop)) break; if (fop->oneframe) break; } if (frame_out > 0) sprite->setTotalFrames(frame_out); fop->createDocument(sprite); return true; }
void CopyCel::onExecute() { LayerImage* srcLayer = static_cast<LayerImage*>(m_srcLayer.layer()); LayerImage* dstLayer = static_cast<LayerImage*>(m_dstLayer.layer()); ASSERT(srcLayer); ASSERT(dstLayer); Sprite* srcSprite = srcLayer->sprite(); Sprite* dstSprite = dstLayer->sprite(); ASSERT(srcSprite); ASSERT(dstSprite); ASSERT(m_srcFrame >= 0 && m_srcFrame < srcSprite->totalFrames()); ASSERT(m_dstFrame >= 0); (void)srcSprite; // To avoid unused variable warning on Release mode Cel* srcCel = srcLayer->cel(m_srcFrame); Cel* dstCel = dstLayer->cel(m_dstFrame); // Clear destination cel if it does exist. It'll be overriden by the // copy of srcCel. if (dstCel) { if (dstCel->links()) executeAndAdd(new cmd::UnlinkCel(dstCel)); executeAndAdd(new cmd::ClearCel(dstCel)); } // Add empty frames until newFrame while (dstSprite->totalFrames() <= m_dstFrame) executeAndAdd(new cmd::AddFrame(dstSprite, dstSprite->totalFrames())); Image* srcImage = (srcCel ? srcCel->image(): NULL); ImageRef dstImage; dstCel = dstLayer->cel(m_dstFrame); if (dstCel) dstImage = dstCel->imageRef(); bool createLink = (srcLayer == dstLayer && dstLayer->isContinuous()); // For background layer if (dstLayer->isBackground()) { ASSERT(dstCel); ASSERT(dstImage); if (!dstCel || !dstImage || !srcCel || !srcImage) return; if (createLink) { executeAndAdd(new cmd::SetCelData(dstCel, srcCel->dataRef())); } else { BlendMode blend = (srcLayer->isBackground() ? BlendMode::SRC: BlendMode::NORMAL); ImageRef tmp(Image::createCopy(dstImage.get())); render::composite_image(tmp.get(), srcImage, srcCel->x(), srcCel->y(), 255, blend); executeAndAdd(new cmd::CopyRect(dstImage.get(), tmp.get(), gfx::Clip(tmp->bounds()))); } } // For transparent layers else { if (dstCel) executeAndAdd(new cmd::RemoveCel(dstCel)); if (srcCel) { if (createLink) dstCel = Cel::createLink(srcCel); else dstCel = Cel::createCopy(srcCel); dstCel->setFrame(m_dstFrame); executeAndAdd(new cmd::AddCel(dstLayer, dstCel)); } } }
bool PixlyFormat::onLoad(FileOp* fop) { try { // load XML metadata XmlDocumentRef doc = open_xml(fop->filename()); TiXmlHandle xml(doc.get()); fop->setProgress(0.25); TiXmlElement* xmlAnim = check(xml.FirstChild("PixlyAnimation").ToElement()); double version = check_number<double>(xmlAnim->Attribute("version")); if (version < 1.5) { throw Exception("version 1.5 or above required"); } TiXmlElement* xmlInfo = check(xmlAnim->FirstChild("Info"))->ToElement(); int layerCount = check_number<int>(xmlInfo->Attribute("layerCount")); int frameWidth = check_number<int>(xmlInfo->Attribute("frameWidth")); int frameHeight = check_number<int>(xmlInfo->Attribute("frameHeight")); UniquePtr<Sprite> sprite(new Sprite(IMAGE_RGB, frameWidth, frameHeight, 0)); TiXmlElement* xmlFrames = check(xmlAnim->FirstChild("Frames"))->ToElement(); int imageCount = check_number<int>(xmlFrames->Attribute("length")); if (layerCount <= 0 || imageCount <= 0) { throw Exception("No cels found"); } int frameCount = imageCount / layerCount; sprite->setTotalFrames(frame_t(frameCount)); sprite->setDurationForAllFrames(200); for (int i=0; i<layerCount; i++) { sprite->folder()->addLayer(new LayerImage(sprite)); } // load image sheet Document* sheet_doc = load_document(nullptr, base::replace_extension(fop->filename(),"png").c_str()); fop->setProgress(0.5); if (sheet_doc == NULL) { throw Exception("Pixly loader requires a valid PNG file"); } Image* sheet = sheet_doc->sprite()->layer(0)->cel(0)->image(); if (sheet->pixelFormat() != IMAGE_RGB) { throw Exception("Pixly loader requires a RGBA PNG"); } int sheetWidth = sheet->width(); int sheetHeight = sheet->height(); // slice cels from sheet std::vector<int> visible(layerCount, 0); TiXmlElement* xmlFrame = check(xmlFrames->FirstChild("Frame"))->ToElement(); while (xmlFrame) { TiXmlElement* xmlRegion = check(xmlFrame->FirstChild("Region"))->ToElement(); TiXmlElement* xmlIndex = check(xmlFrame->FirstChild("Index"))->ToElement(); int index = check_number<int>(xmlIndex->Attribute("linear")); frame_t frame(index / layerCount); LayerIndex layer_index(index % layerCount); Layer *layer = sprite->indexToLayer(layer_index); const char * duration = xmlFrame->Attribute("duration"); if (duration) { sprite->setFrameDuration(frame, base::convert_to<int>(std::string(duration))); } visible[(int)layer_index] += (int)(std::string(check(xmlFrame->Attribute("visible"),"false")) == "true"); int x0 = check_number<int>(xmlRegion->Attribute("x")); int y0_up = check_number<int>(xmlRegion->Attribute("y")); // inverted if (y0_up < 0 || y0_up + frameHeight > sheetHeight || x0 < 0 || x0 + frameWidth > sheetWidth) { throw Exception("looking for cels outside the bounds of the PNG"); } // read cel images ImageRef image(Image::create(IMAGE_RGB, frameWidth, frameHeight)); for (int y = 0; y < frameHeight; y++) { // RGB_ALPHA int y0_down = sheetHeight-1 - y0_up - (frameHeight-1) + y; uint32_t* src_begin = (uint32_t*)sheet->getPixelAddress(x0 , y0_down); uint32_t* src_end = (uint32_t*)sheet->getPixelAddress(x0+frameWidth, y0_down); uint32_t* dst_begin = (uint32_t*)image->getPixelAddress(0, y); std::copy(src_begin, src_end, dst_begin); } // make cel trimmed or empty gfx::Rect bounds; if (algorithm::shrink_bounds(image.get(), bounds, image->maskColor())) { ImageRef trim_image(crop_image(image.get(), bounds.x, bounds.y, bounds.w, bounds.h, image->maskColor())); Cel* cel = NULL; if ((int)frame > 0) { // link identical neighbors Cel *prev_cel = static_cast<LayerImage*>(layer)->cel(frame-1); if (prev_cel && prev_cel->x() == bounds.x && prev_cel->y() == bounds.y) { Image *prev_image = prev_cel->image(); if (prev_image && doc::count_diff_between_images(prev_image, trim_image.get()) == 0) { cel = Cel::createLink(prev_cel); cel->setFrame(frame); } // count_diff_between_images } // prev_cel } // frame > 0 if (cel == NULL) { cel = new Cel(frame, trim_image); cel->setPosition(bounds.x, bounds.y); } static_cast<LayerImage*>(layer)->addCel(cel); } xmlFrame = xmlFrame->NextSiblingElement(); fop->setProgress(0.5 + 0.5 * ((float)(index+1) / (float)imageCount)); } for (int i=0; i<layerCount; i++) { LayerIndex layer_index(i); Layer *layer = sprite->indexToLayer(layer_index); layer->setVisible(visible[i] > frameCount/2); } fop->createDocument(sprite); sprite.release(); } catch(Exception &e) { fop->setError((std::string("Pixly file format: ")+std::string(e.what())+"\n").c_str()); return false; } return true; }