void ImportSpriteSheetCommand::onExecute(Context* context) { ImportSpriteSheetWindow window(context); window.openWindowInForeground(); if (!window.ok()) return; Doc* document = window.document(); DocumentPreferences* docPref = window.docPref(); gfx::Rect frameBounds = window.frameBounds(); gfx::Size padThickness = window.paddingThickness(); bool partialTiles = window.partialTilesValue(); bool paddingEnable = window.paddingEnabledValue(); auto sheetType = window.sheetTypeValue(); ASSERT(document); if (!document) return; // The list of frames imported from the sheet std::vector<ImageRef> animation; try { Sprite* sprite = document->sprite(); frame_t currentFrame = context->activeSite().frame(); render::Render render; render.setNewBlend(Preferences::instance().experimental.newBlend()); // Each sprite in the sheet std::vector<gfx::Rect> tileRects; int widthStop = sprite->width(); int heightStop = sprite->height(); if (partialTiles) { widthStop += frameBounds.w-1; heightStop += frameBounds.h-1; } switch (sheetType) { case app::SpriteSheetType::Horizontal: for (int x=frameBounds.x; x+frameBounds.w<=widthStop; x+=frameBounds.w+padThickness.w) { tileRects.push_back(gfx::Rect(x, frameBounds.y, frameBounds.w, frameBounds.h)); } break; case app::SpriteSheetType::Vertical: for (int y=frameBounds.y; y+frameBounds.h<=heightStop; y+=frameBounds.h+padThickness.h) { tileRects.push_back(gfx::Rect(frameBounds.x, y, frameBounds.w, frameBounds.h)); } break; case app::SpriteSheetType::Rows: for (int y=frameBounds.y; y+frameBounds.h<=heightStop; y+=frameBounds.h+padThickness.h) { for (int x=frameBounds.x; x+frameBounds.w<=widthStop; x+=frameBounds.w+padThickness.w) { tileRects.push_back(gfx::Rect(x, y, frameBounds.w, frameBounds.h)); } } break; case app::SpriteSheetType::Columns: for (int x=frameBounds.x; x+frameBounds.w<=widthStop; x+=frameBounds.w+padThickness.w) { for (int y=frameBounds.y; y+frameBounds.h<=heightStop; y+=frameBounds.h+padThickness.h) { tileRects.push_back(gfx::Rect(x, y, frameBounds.w, frameBounds.h)); } } break; } // As first step, we cut each tile and add them into "animation" list. for (const auto& tileRect : tileRects) { ImageRef resultImage( Image::create( sprite->pixelFormat(), tileRect.w, tileRect.h)); // Render the portion of sheet. render.renderSprite( resultImage.get(), sprite, currentFrame, gfx::Clip(0, 0, tileRect)); animation.push_back(resultImage); } if (animation.size() == 0) { Alert::show(Strings::alerts_empty_rect_importing_sprite_sheet()); return; } // The following steps modify the sprite, so we wrap all // operations in a undo-transaction. ContextWriter writer(context); Tx tx(writer.context(), "Import Sprite Sheet", ModifyDocument); DocApi api = document->getApi(tx); // Add the layer in the sprite. LayerImage* resultLayer = api.newLayer(sprite->root(), "Sprite Sheet"); // Add all frames+cels to the new layer for (size_t i=0; i<animation.size(); ++i) { // Create the cel. std::unique_ptr<Cel> resultCel(new Cel(frame_t(i), animation[i])); // Add the cel in the layer. api.addCel(resultLayer, resultCel.get()); resultCel.release(); } // Copy the list of layers (because we will modify it in the iteration). LayerList layers = sprite->root()->layers(); // Remove all other layers for (Layer* child : layers) { if (child != resultLayer) api.removeLayer(child); } // Change the number of frames api.setTotalFrames(sprite, frame_t(animation.size())); // Set the size of the sprite to the tile size. api.setSpriteSize(sprite, frameBounds.w, frameBounds.h); tx.commit(); ASSERT(docPref); if (docPref) { docPref->importSpriteSheet.type(sheetType); docPref->importSpriteSheet.bounds(frameBounds); docPref->importSpriteSheet.partialTiles(partialTiles); docPref->importSpriteSheet.paddingBounds(padThickness); docPref->importSpriteSheet.paddingEnabled(paddingEnable); } } catch (...) { throw; } update_screen_for_document(document); }
void SaveFileCopyAsCommand::onExecute(Context* context) { Doc* doc = context->activeDocument(); std::string outputFilename = m_filename; std::string layers = kAllLayers; std::string frames = kAllFrames; double xscale = 1.0; double yscale = 1.0; bool applyPixelRatio = false; doc::AniDir aniDirValue = convert_string_to_anidir(m_aniDir); bool isForTwitter = false; #if ENABLE_UI if (context->isUIAvailable()) { ExportFileWindow win(doc); bool askOverwrite = true; win.SelectOutputFile.connect( [this, &win, &askOverwrite, context, doc]() -> std::string { std::string result = saveAsDialog( context, "Export", win.outputFilenameValue(), false, false, (doc->isAssociatedToFile() ? doc->filename(): std::string())); if (!result.empty()) askOverwrite = false; // Already asked in the file selector dialog return result; }); again:; if (!win.show()) return; outputFilename = win.outputFilenameValue(); if (askOverwrite && base::is_file(outputFilename)) { int ret = OptionalAlert::show( Preferences::instance().exportFile.showOverwriteFilesAlert, 1, // Yes is the default option when the alert dialog is disabled fmt::format(Strings::alerts_overwrite_files_on_export(), outputFilename)); if (ret != 1) goto again; } // Save the preferences used to export the file, so if we open the // window again, we will have the same options. win.savePref(); layers = win.layersValue(); frames = win.framesValue(); xscale = yscale = win.resizeValue(); applyPixelRatio = win.applyPixelRatio(); aniDirValue = win.aniDirValue(); isForTwitter = win.isForTwitter(); } #endif // Pixel ratio if (applyPixelRatio) { doc::PixelRatio pr = doc->sprite()->pixelRatio(); xscale *= pr.w; yscale *= pr.h; } // Apply scale const undo::UndoState* undoState = nullptr; bool undoResize = false; if (xscale != 1.0 || yscale != 1.0) { Command* resizeCmd = Commands::instance()->byId(CommandId::SpriteSize()); ASSERT(resizeCmd); if (resizeCmd) { int width = doc->sprite()->width(); int height = doc->sprite()->height(); int newWidth = int(double(width) * xscale); int newHeight = int(double(height) * yscale); if (newWidth < 1) newWidth = 1; if (newHeight < 1) newHeight = 1; if (width != newWidth || height != newHeight) { doc->setInhibitBackup(true); undoState = doc->undoHistory()->currentState(); undoResize = true; Params params; params.set("use-ui", "false"); params.set("width", base::convert_to<std::string>(newWidth).c_str()); params.set("height", base::convert_to<std::string>(newHeight).c_str()); params.set("resize-method", "nearest-neighbor"); // TODO add algorithm in the UI? context->executeCommand(resizeCmd, params); } } } { RestoreVisibleLayers layersVisibility; if (context->isUIAvailable()) { Site site = context->activeSite(); // Selected layers to export calculate_visible_layers(site, layers, layersVisibility); // Selected frames to export SelectedFrames selFrames; FrameTag* frameTag = calculate_selected_frames( site, frames, selFrames); if (frameTag) m_frameTag = frameTag->name(); m_selFrames = selFrames; m_adjustFramesByFrameTag = false; } base::ScopedValue<std::string> restoreAniDir( m_aniDir, convert_anidir_to_string(aniDirValue), // New value m_aniDir); // Restore old value // TODO This should be set as options for the specific encoder GifEncoderDurationFix fixGif(isForTwitter); PngEncoderOneAlphaPixel fixPng(isForTwitter); saveDocumentInBackground( context, doc, outputFilename, false); } // Undo resize if (undoResize && undoState != doc->undoHistory()->currentState()) { moveToUndoState(doc, undoState); doc->setInhibitBackup(false); } }