void ImportSpriteSheetCommand::onExecute(Context* context) { ImportSpriteSheetWindow window(context); retry:; window.openWindowInForeground(); if (!window.ok()) return; Document* document = window.document(); DocumentPreferences* docPref = window.docPref(); gfx::Rect frameBounds = window.frameBounds(); // The user don't select a sheet yet. if (!document) { Alert::show("Import Sprite Sheet<<Select a sprite first.||&Close"); goto retry; } // 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; // As first step, we cut each tile and add them into "animation" list. for (int y=frameBounds.y; y<sprite->height(); y += frameBounds.h) { for (int x=frameBounds.x; x<sprite->width(); x += frameBounds.w) { ImageRef resultImage( Image::create(sprite->pixelFormat(), frameBounds.w, frameBounds.h)); // Render the portion of sheet. render.renderSprite(resultImage.get(), sprite, currentFrame, gfx::Clip(0, 0, x, y, frameBounds.w, frameBounds.h)); animation.push_back(resultImage); } } if (animation.size() == 0) { Alert::show("Import Sprite Sheet" "<<The specified rectangle does not create any tile." "<<Select a rectangle inside the sprite region." "||&OK"); return; } // The following steps modify the sprite, so we wrap all // operations in a undo-transaction. ContextWriter writer(context); Transaction transaction(writer.context(), "Import Sprite Sheet", ModifyDocument); DocumentApi api = document->getApi(transaction); // Add the layer in the sprite. LayerImage* resultLayer = api.newLayer(sprite); // Add all frames+cels to the new layer for (size_t i=0; i<animation.size(); ++i) { // Create the cel. base::UniquePtr<Cel> resultCel(new Cel(frame_t(i), animation[i])); // Add the cel in the layer. api.addCel(resultLayer, resultCel); resultCel.release(); } // Copy the list of layers (because we will modify it in the iteration). LayerList layers = sprite->folder()->getLayersList(); // Remove all other layers for (LayerIterator it=layers.begin(), end=layers.end(); it!=end; ++it) { if (*it != resultLayer) api.removeLayer(*it); } // 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); transaction.commit(); ASSERT(docPref); if (docPref) docPref->importSpriteSheet.bounds(frameBounds); } catch (...) { throw; } update_screen_for_document(document); }
/** * [working thread] */ virtual void onJob() { UndoTransaction undoTransaction(m_writer.context(), "Rotate Canvas"); DocumentApi api = m_document->getApi(); // get all sprite cels CelList cels; m_sprite->getCels(cels); // for each cel... for (CelIterator it = cels.begin(); it != cels.end(); ++it) { Cel* cel = *it; Image* image = m_sprite->getStock()->getImage(cel->getImage()); // change it location switch (m_angle) { case 180: api.setCelPosition(m_sprite, cel, m_sprite->getWidth() - cel->getX() - image->getWidth(), m_sprite->getHeight() - cel->getY() - image->getHeight()); break; case 90: api.setCelPosition(m_sprite, cel, m_sprite->getHeight() - cel->getY() - image->getHeight(), cel->getX()); break; case -90: api.setCelPosition(m_sprite, cel, cel->getY(), m_sprite->getWidth() - cel->getX() - image->getWidth()); break; } } // for each stock's image for (int i=0; i<m_sprite->getStock()->size(); ++i) { Image* image = m_sprite->getStock()->getImage(i); if (!image) continue; // rotate the image Image* new_image = Image::create(image->getPixelFormat(), m_angle == 180 ? image->getWidth(): image->getHeight(), m_angle == 180 ? image->getHeight(): image->getWidth()); raster::rotate_image(image, new_image, m_angle); api.replaceStockImage(m_sprite, i, new_image); jobProgress((float)i / m_sprite->getStock()->size()); // cancel all the operation? if (isCanceled()) return; // UndoTransaction destructor will undo all operations } // rotate mask if (m_document->isMaskVisible()) { Mask* origMask = m_document->getMask(); base::UniquePtr<Mask> new_mask(new Mask()); const gfx::Rect& origBounds = origMask->getBounds(); int x = 0, y = 0; switch (m_angle) { case 180: x = m_sprite->getWidth() - origBounds.x - origBounds.w; y = m_sprite->getHeight() - origBounds.y - origBounds.h; break; case 90: x = m_sprite->getHeight() - origBounds.y - origBounds.h; y = origBounds.x; break; case -90: x = origBounds.y; y = m_sprite->getWidth() - origBounds.x - origBounds.w; break; } // create the new rotated mask new_mask->replace(x, y, m_angle == 180 ? origBounds.w: origBounds.h, m_angle == 180 ? origBounds.h: origBounds.w); raster::rotate_image(origMask->getBitmap(), new_mask->getBitmap(), m_angle); // Copy new mask api.copyToCurrentMask(new_mask); // Regenerate mask m_document->resetTransformation(); m_document->generateMaskBoundaries(); } // change the sprite's size if (m_angle != 180) api.setSpriteSize(m_sprite, m_sprite->getHeight(), m_sprite->getWidth()); // commit changes undoTransaction.commit(); }
/** * [working thread] */ virtual void onJob() { UndoTransaction undoTransaction(m_writer.context(), "Sprite Size"); DocumentApi api = m_writer.document()->getApi(); // Get all sprite cels CelList cels; m_sprite->getCels(cels); // For each cel... int progress = 0; for (CelIterator it = cels.begin(); it != cels.end(); ++it, ++progress) { Cel* cel = *it; // Change its location api.setCelPosition(m_sprite, cel, scale_x(cel->x()), scale_y(cel->y())); // Get cel's image Image* image = cel->image(); if (!image) continue; // Resize the image int w = scale_x(image->width()); int h = scale_y(image->height()); Image* new_image = Image::create(image->pixelFormat(), MAX(1, w), MAX(1, h)); doc::algorithm::fixup_image_transparent_colors(image); doc::algorithm::resize_image(image, new_image, m_resize_method, m_sprite->getPalette(cel->frame()), m_sprite->getRgbMap(cel->frame())); api.replaceStockImage(m_sprite, cel->imageIndex(), new_image); jobProgress((float)progress / cels.size()); // cancel all the operation? if (isCanceled()) return; // UndoTransaction destructor will undo all operations } // Resize mask if (m_document->isMaskVisible()) { base::UniquePtr<Image> old_bitmap (crop_image(m_document->mask()->bitmap(), -1, -1, m_document->mask()->bitmap()->width()+2, m_document->mask()->bitmap()->height()+2, 0)); int w = scale_x(old_bitmap->width()); int h = scale_y(old_bitmap->height()); base::UniquePtr<Mask> new_mask(new Mask); new_mask->replace(scale_x(m_document->mask()->bounds().x-1), scale_y(m_document->mask()->bounds().y-1), MAX(1, w), MAX(1, h)); algorithm::resize_image(old_bitmap, new_mask->bitmap(), m_resize_method, m_sprite->getPalette(FrameNumber(0)), // Ignored m_sprite->getRgbMap(FrameNumber(0))); // Ignored // Reshrink new_mask->intersect(new_mask->bounds()); // Copy new mask api.copyToCurrentMask(new_mask); // Regenerate mask m_document->resetTransformation(); m_document->generateMaskBoundaries(); } // resize sprite api.setSpriteSize(m_sprite, m_new_width, m_new_height); // commit changes undoTransaction.commit(); }
// [working thread] virtual void onJob() { Transaction transaction(m_writer.context(), "Rotate Canvas"); DocumentApi api = m_document->getApi(transaction); // 1) Rotate cel positions for (Cel* cel : m_cels) { Image* image = cel->image(); if (!image) continue; switch (m_angle) { case 180: api.setCelPosition(m_sprite, cel, m_sprite->width() - cel->x() - image->width(), m_sprite->height() - cel->y() - image->height()); break; case 90: api.setCelPosition(m_sprite, cel, m_sprite->height() - cel->y() - image->height(), cel->x()); break; case -90: api.setCelPosition(m_sprite, cel, cel->y(), m_sprite->width() - cel->x() - image->width()); break; } } // 2) Rotate images int i = 0; for (Cel* cel : m_cels) { Image* image = cel->image(); if (image) { ImageRef new_image(Image::create(image->pixelFormat(), m_angle == 180 ? image->width(): image->height(), m_angle == 180 ? image->height(): image->width())); doc::rotate_image(image, new_image.get(), m_angle); api.replaceImage(m_sprite, cel->imageRef(), new_image); } jobProgress((float)i / m_cels.size()); ++i; // cancel all the operation? if (isCanceled()) return; // Transaction destructor will undo all operations } // rotate mask if (m_document->isMaskVisible()) { Mask* origMask = m_document->mask(); base::UniquePtr<Mask> new_mask(new Mask()); const gfx::Rect& origBounds = origMask->bounds(); int x = 0, y = 0; switch (m_angle) { case 180: x = m_sprite->width() - origBounds.x - origBounds.w; y = m_sprite->height() - origBounds.y - origBounds.h; break; case 90: x = m_sprite->height() - origBounds.y - origBounds.h; y = origBounds.x; break; case -90: x = origBounds.y; y = m_sprite->width() - origBounds.x - origBounds.w; break; } // create the new rotated mask new_mask->replace( gfx::Rect(x, y, m_angle == 180 ? origBounds.w: origBounds.h, m_angle == 180 ? origBounds.h: origBounds.w)); doc::rotate_image(origMask->bitmap(), new_mask->bitmap(), m_angle); // Copy new mask api.copyToCurrentMask(new_mask); // Regenerate mask m_document->resetTransformation(); m_document->generateMaskBoundaries(); } // change the sprite's size if (m_rotateSprite && m_angle != 180) api.setSpriteSize(m_sprite, m_sprite->height(), m_sprite->width()); // commit changes transaction.commit(); }
void onImport() { // The user don't select a sheet yet. if (!m_document) { Alert::show("Import Sprite Sheet<<Select a sprite first.||&Close"); return; } // The list of frames imported from the sheet std::vector<Image*> animation; try { Sprite* sprite = m_document->getSprite(); FrameNumber currentFrame = m_context->getActiveLocation().frame(); // As first step, we cut each tile and add them into "animation" list. for (int y=m_rect.y; y<sprite->getHeight(); y += m_rect.h) { for (int x=m_rect.x; x<sprite->getWidth(); x += m_rect.w) { base::UniquePtr<Image> resultImage(Image::create(sprite->getPixelFormat(), m_rect.w, m_rect.h)); // Clear the image with mask color. image_clear(resultImage, 0); // Render the portion of sheet. sprite->render(resultImage, -x, -y, currentFrame); animation.push_back(resultImage); resultImage.release(); } } if (animation.size() == 0) { Alert::show("Import Sprite Sheet" "<<The specified rectangle does not create any tile." "<<Select a rectangle inside the sprite region." "||&OK"); return; } // The following steps modify the sprite, so we wrap all // operations in a undo-transaction. ContextWriter writer(m_context); UndoTransaction undoTransaction(writer.context(), "Import Sprite Sheet", undo::ModifyDocument); DocumentApi api = m_document->getApi(); // Add the layer in the sprite. LayerImage* resultLayer = api.newLayer(sprite); // Add all frames+cels to the new layer for (size_t i=0; i<animation.size(); ++i) { int indexInStock; // Add the image into the sprite's stock indexInStock = api.addImageInStock(sprite, animation[i]); animation[i] = NULL; // Create the cel. base::UniquePtr<Cel> resultCel(new Cel(FrameNumber(i), indexInStock)); // Add the cel in the layer. api.addCel(resultLayer, resultCel); resultCel.release(); } // Copy the list of layers (because we will modify it in the iteration). LayerList layers = sprite->getFolder()->getLayersList(); // Remove all other layers for (LayerIterator it=layers.begin(), end=layers.end(); it!=end; ++it) { if (*it != resultLayer) api.removeLayer(*it); } // Change the number of frames api.setTotalFrames(sprite, FrameNumber(animation.size())); // Set the size of the sprite to the tile size. api.setSpriteSize(sprite, m_rect.w, m_rect.h); undoTransaction.commit(); } catch (...) { for (size_t i=0; i<animation.size(); ++i) delete animation[i]; throw; } update_screen_for_document(m_document); closeWindow(NULL); }
// [working thread] void onJob() override { DocumentApi api = writer().document()->getApi(transaction()); int cels_count = 0; for (Cel* cel : sprite()->uniqueCels()) { // TODO add size() member function to CelsRange (void)cel; ++cels_count; } // For each cel... int progress = 0; for (Cel* cel : sprite()->uniqueCels()) { // Get cel's image Image* image = cel->image(); if (image && !cel->link()) { // Resize the cel bounds only if it's from a reference layer if (cel->layer()->isReference()) { gfx::RectF newBounds = scale_rect<double>(cel->boundsF()); transaction().execute(new cmd::SetCelBoundsF(cel, newBounds)); } else { // Change its location api.setCelPosition(sprite(), cel, scale_x(cel->x()), scale_y(cel->y())); // Resize the image int w = scale_x(image->width()); int h = scale_y(image->height()); ImageRef new_image(Image::create(image->pixelFormat(), MAX(1, w), MAX(1, h))); new_image->setMaskColor(image->maskColor()); doc::algorithm::fixup_image_transparent_colors(image); doc::algorithm::resize_image( image, new_image.get(), m_resize_method, sprite()->palette(cel->frame()), sprite()->rgbMap(cel->frame()), (cel->layer()->isBackground() ? -1: sprite()->transparentColor())); api.replaceImage(sprite(), cel->imageRef(), new_image); } } jobProgress((float)progress / cels_count); ++progress; // Cancel all the operation? if (isCanceled()) return; // Transaction destructor will undo all operations } // Resize mask if (document()->isMaskVisible()) { ImageRef old_bitmap (crop_image(document()->mask()->bitmap(), -1, -1, document()->mask()->bitmap()->width()+2, document()->mask()->bitmap()->height()+2, 0)); int w = scale_x(old_bitmap->width()); int h = scale_y(old_bitmap->height()); base::UniquePtr<Mask> new_mask(new Mask); new_mask->replace( gfx::Rect( scale_x(document()->mask()->bounds().x-1), scale_y(document()->mask()->bounds().y-1), MAX(1, w), MAX(1, h))); algorithm::resize_image( old_bitmap.get(), new_mask->bitmap(), m_resize_method, sprite()->palette(0), // Ignored sprite()->rgbMap(0), // Ignored -1); // Ignored // Reshrink new_mask->intersect(new_mask->bounds()); // Copy new mask api.copyToCurrentMask(new_mask); // Regenerate mask document()->resetTransformation(); document()->generateMaskBoundaries(); } // Resize slices for (auto& slice : sprite()->slices()) { for (auto& k : *slice) { const SliceKey& key = *k.value(); if (key.isEmpty()) continue; SliceKey newKey = key; newKey.setBounds(scale_rect(newKey.bounds())); if (newKey.hasCenter()) newKey.setCenter(scale_rect(newKey.center())); if (newKey.hasPivot()) newKey.setPivot(gfx::Point(scale_x(newKey.pivot().x), scale_y(newKey.pivot().y))); transaction().execute( new cmd::SetSliceKey(slice, k.frame(), newKey)); } } // Resize Sprite api.setSpriteSize(sprite(), m_new_width, m_new_height); }
void ImportSpriteSheetCommand::onExecute(Context* context) { ImportSpriteSheetWindow window(context); window.openWindowInForeground(); if (!window.ok()) return; Document* document = window.document(); DocumentPreferences* docPref = window.docPref(); gfx::Rect frameBounds = window.frameBounds(); bool partialTiles = window.partialTilesValue(); 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; // 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) { 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) { 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) { for (int x=frameBounds.x; x+frameBounds.w<=widthStop; x += frameBounds.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<=sprite->width(); x += frameBounds.w) { for (int y=frameBounds.y; y+frameBounds.h<=sprite->height(); y += frameBounds.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("Import Sprite Sheet" "<<The specified rectangle does not create any tile." "<<Select a rectangle inside the sprite region." "||&OK"); return; } // The following steps modify the sprite, so we wrap all // operations in a undo-transaction. ContextWriter writer(context); Transaction transaction(writer.context(), "Import Sprite Sheet", ModifyDocument); DocumentApi api = document->getApi(transaction); // Add the layer in the sprite. LayerImage* resultLayer = api.newLayer(sprite, "Sprite Sheet"); // Add all frames+cels to the new layer for (size_t i=0; i<animation.size(); ++i) { // Create the cel. base::UniquePtr<Cel> resultCel(new Cel(frame_t(i), animation[i])); // Add the cel in the layer. api.addCel(resultLayer, resultCel); resultCel.release(); } // Copy the list of layers (because we will modify it in the iteration). LayerList layers = sprite->folder()->getLayersList(); // Remove all other layers for (LayerIterator it=layers.begin(), end=layers.end(); it!=end; ++it) { if (*it != resultLayer) api.removeLayer(*it); } // 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); transaction.commit(); ASSERT(docPref); if (docPref) { docPref->importSpriteSheet.type(sheetType); docPref->importSpriteSheet.bounds(frameBounds); docPref->importSpriteSheet.partialTiles(partialTiles); } } catch (...) { throw; } update_screen_for_document(document); }