void ClearMask::clear() { Cel* cel = this->cel(); Image* image = m_dstImage->image(); app::Document* doc = static_cast<app::Document*>(cel->document()); Mask* mask = doc->mask(); ASSERT(mask->bitmap()); if (!mask->bitmap()) return; const LockImageBits<BitmapTraits> maskBits(mask->bitmap()); LockImageBits<BitmapTraits>::const_iterator it = maskBits.begin(); // Clear the masked zones int u, v; for (v=0; v<mask->bounds().h; ++v) { for (u=0; u<mask->bounds().w; ++u, ++it) { ASSERT(it != maskBits.end()); if (*it) { put_pixel(image, u + m_offsetX, v + m_offsetY, m_bgcolor); } } } ASSERT(it == maskBits.end()); }
void stroke_selection(Image* image, const gfx::Point& offset, const Mask* origMask, const color_t color) { ASSERT(origMask); ASSERT(origMask->bitmap()); if (!origMask || !origMask->bitmap()) return; gfx::Rect bounds = origMask->bounds(); if (bounds.isEmpty()) return; Mask mask; mask.reserve(bounds); mask.freeze(); modify_selection( SelectionModifier::Border, origMask, &mask, 1, BrushType::kCircleBrushType); mask.unfreeze(); // Both mask must have the same bounds. ASSERT(mask.bounds() == origMask->bounds()); if (mask.bitmap()) fill_selection(image, offset, &mask, color); }
void InvertMaskCommand::onExecute(Context* context) { bool hasMask = false; { const ContextReader reader(context); if (reader.document()->isMaskVisible()) hasMask = true; } // without mask?... if (!hasMask) { // so we select all Command* mask_all_cmd = CommandsModule::instance()->getCommandByName(CommandId::MaskAll); context->executeCommand(mask_all_cmd); } // invert the current mask else { ContextWriter writer(context); Document* document(writer.document()); Sprite* sprite(writer.sprite()); // Select all the sprite area base::UniquePtr<Mask> mask(new Mask()); mask->replace(sprite->bounds()); // Remove in the new mask the current sprite marked region const gfx::Rect& maskBounds = document->mask()->bounds(); doc::fill_rect(mask->bitmap(), maskBounds.x, maskBounds.y, maskBounds.x + maskBounds.w-1, maskBounds.y + maskBounds.h-1, 0); Mask* curMask = document->mask(); if (curMask->bitmap()) { // Copy the inverted region in the new mask (we just modify the // document's mask temporaly here) curMask->freeze(); curMask->invert(); doc::copy_image(mask->bitmap(), curMask->bitmap(), curMask->bounds().x, curMask->bounds().y); curMask->invert(); curMask->unfreeze(); } // We need only need the area inside the sprite mask->intersect(sprite->bounds()); // Set the new mask Transaction transaction(writer.context(), "Mask Invert", DoesntModifyDocument); transaction.execute(new cmd::SetMask(document, mask)); transaction.commit(); document->generateMaskBoundaries(); update_screen_for_document(document); } }
ClearMask::ClearMask(Cel* cel) : WithCel(cel) { Doc* doc = static_cast<Doc*>(cel->document()); // If the mask is empty or is not visible then we have to clear the // entire image in the cel. if (!doc->isMaskVisible()) { m_seq.add(new cmd::ClearCel(cel)); return; } Image* image = cel->image(); assert(image); if (!image) return; Mask* mask = doc->mask(); m_offset = mask->bounds().origin() - cel->position(); gfx::Rect bounds = image->bounds().createIntersection( gfx::Rect( m_offset.x, m_offset.y, mask->bounds().w, mask->bounds().h)); if (bounds.isEmpty()) return; m_dstImage.reset(new WithImage(image)); m_bgcolor = doc->bgColor(cel->layer()); m_boundsX = bounds.x; m_boundsY = bounds.y; m_copy.reset(crop_image(image, bounds.x, bounds.y, bounds.w, bounds.h, m_bgcolor)); }
bool StandbyState::onUpdateStatusBar(Editor* editor) { tools::Ink* ink = editor->getCurrentEditorInk(); const Sprite* sprite = editor->sprite(); int x, y; editor->screenToEditor(jmouse_x(0), jmouse_y(0), &x, &y); if (!sprite) { StatusBar::instance()->clearText(); } // For eye-dropper else if (ink->isEyedropper()) { bool grabAlpha = UIContext::instance()->settings()->getGrabAlpha(); ColorPicker picker; picker.pickColor(editor->getDocumentLocation(), x, y, grabAlpha ? ColorPicker::FromActiveLayer: ColorPicker::FromComposition); char buf[256]; usprintf(buf, "- Pos %d %d", x, y); StatusBar::instance()->showColor(0, buf, picker.color(), picker.alpha()); } else { Mask* mask = (editor->document()->isMaskVisible() ? editor->document()->mask(): NULL); StatusBar::instance()->setStatusText(0, "Pos %d %d, Size %d %d, Frame %d [%d msecs]", x, y, (mask ? mask->bounds().w: sprite->width()), (mask ? mask->bounds().h: sprite->height()), editor->frame()+1, sprite->getFrameDuration(editor->frame())); } return true; }
bool StandbyState::onUpdateStatusBar(Editor* editor) { tools::Ink* ink = editor->getCurrentEditorInk(); const Sprite* sprite = editor->sprite(); gfx::Point spritePos = editor->screenToEditor(ui::get_mouse_position()); if (!sprite) { StatusBar::instance()->clearText(); } // For eye-dropper else if (ink->isEyedropper()) { bool grabAlpha = Preferences::instance().editor.grabAlpha(); ColorPicker picker; picker.pickColor(editor->getSite(), spritePos, grabAlpha ? ColorPicker::FromActiveLayer: ColorPicker::FromComposition); char buf[256]; sprintf(buf, "- Pos %d %d", spritePos.x, spritePos.y); StatusBar::instance()->showColor(0, buf, picker.color(), picker.alpha()); } else { Mask* mask = (editor->document()->isMaskVisible() ? editor->document()->mask(): NULL); StatusBar::instance()->setStatusText(0, "Pos %d %d, Size %d %d, Frame %d [%d msecs]", spritePos.x, spritePos.y, (mask ? mask->bounds().w: sprite->width()), (mask ? mask->bounds().h: sprite->height()), editor->frame()+1, sprite->frameDuration(editor->frame())); } return true; }
EditorState::LeaveAction MovingSelectionState::onLeaveState(Editor* editor, EditorState* newState) { Document* doc = editor->document(); Mask* mask = doc->mask(); gfx::Point newOrigin = mask->bounds().origin(); // Restore the mask to the original state so we can transform it // with the a undoable transaction. mask->setOrigin(m_selOrigin.x, m_selOrigin.y); mask->unfreeze(); { ContextWriter writer(UIContext::instance(), 1000); Transaction transaction(writer.context(), "Move Selection Edges", DoesntModifyDocument); transaction.execute(new cmd::SetMaskPosition(doc, newOrigin)); transaction.commit(); } doc->resetTransformation(); doc->notifyGeneralUpdate(); return StandbyState::onLeaveState(editor, newState); }
bool StandbyState::onUpdateStatusBar(Editor* editor) { tools::Ink* ink = editor->getCurrentEditorInk(); const Sprite* sprite = editor->sprite(); gfx::Point spritePos = editor->screenToEditor(ui::get_mouse_position()); if (!sprite) { StatusBar::instance()->clearText(); } // For eye-dropper else if (ink->isEyedropper()) { EyedropperCommand cmd; app::Color color = Preferences::instance().colorBar.fgColor(); cmd.pickSample(editor->getSite(), spritePos, color); char buf[256]; sprintf(buf, "- Pos %d %d", spritePos.x, spritePos.y); StatusBar::instance()->showColor(0, buf, color); } else { Mask* mask = (editor->document()->isMaskVisible() ? editor->document()->mask(): NULL); StatusBar::instance()->setStatusText(0, "Pos %d %d, Size %d %d, Frame %d [%d msecs]", spritePos.x, spritePos.y, (mask ? mask->bounds().w: sprite->width()), (mask ? mask->bounds().h: sprite->height()), editor->frame()+1, sprite->frameDuration(editor->frame())); } return true; }
void FlipCommand::onExecute(Context* context) { ContextWriter writer(context); Document* document = writer.document(); Sprite* sprite = writer.sprite(); { Transaction transaction(writer.context(), m_flipMask ? (m_flipType == doc::algorithm::FlipHorizontal ? "Flip Horizontal": "Flip Vertical"): (m_flipType == doc::algorithm::FlipHorizontal ? "Flip Canvas Horizontal": "Flip Canvas Vertical")); DocumentApi api = document->getApi(transaction); CelList cels; if (m_flipMask) { auto range = App::instance()->timeline()->range(); if (range.enabled()) cels = get_unique_cels(sprite, range); else if (writer.cel()) cels.push_back(writer.cel()); } else { for (Cel* cel : sprite->uniqueCels()) cels.push_back(cel); } Mask* mask = document->mask(); if (m_flipMask && document->isMaskVisible()) { Site site = *writer.site(); for (Cel* cel : cels) { site.frame(cel->frame()); site.layer(cel->layer()); int x, y; Image* image = site.image(&x, &y); if (!image) continue; // When the mask is inside the cel, we can try to flip the // pixels inside the image. if (cel->bounds().contains(mask->bounds())) { gfx::Rect flipBounds = mask->bounds(); flipBounds.offset(-x, -y); flipBounds &= image->bounds(); if (flipBounds.isEmpty()) continue; if (mask->bitmap() && !mask->isRectangular()) transaction.execute(new cmd::FlipMaskedCel(cel, m_flipType)); else api.flipImage(image, flipBounds, m_flipType); if (cel->layer()->isTransparent()) transaction.execute(new cmd::TrimCel(cel)); } // When the mask is bigger than the cel bounds, we have to // expand the cel, make the flip, and shrink it again. else { gfx::Rect flipBounds = (sprite->bounds() & mask->bounds()); if (flipBounds.isEmpty()) continue; ExpandCelCanvas expand( site, cel->layer(), TiledMode::NONE, transaction, ExpandCelCanvas::None); expand.validateDestCanvas(gfx::Region(flipBounds)); if (mask->bitmap() && !mask->isRectangular()) doc::algorithm::flip_image_with_mask( expand.getDestCanvas(), mask, m_flipType, document->bgColor(cel->layer())); else doc::algorithm::flip_image( expand.getDestCanvas(), flipBounds, m_flipType); expand.commit(); } } } else { for (Cel* cel : cels) { Image* image = cel->image(); api.setCelPosition (sprite, cel, (m_flipType == doc::algorithm::FlipHorizontal ? sprite->width() - image->width() - cel->x(): cel->x()), (m_flipType == doc::algorithm::FlipVertical ? sprite->height() - image->height() - cel->y(): cel->y())); api.flipImage(image, image->bounds(), m_flipType); } } // Flip the mask. Image* maskBitmap = mask->bitmap(); if (maskBitmap) { transaction.execute(new cmd::FlipMask(document, m_flipType)); // Flip the mask position because the if (!m_flipMask) transaction.execute( new cmd::SetMaskPosition( document, gfx::Point( (m_flipType == doc::algorithm::FlipHorizontal ? sprite->width() - mask->bounds().x2(): mask->bounds().x), (m_flipType == doc::algorithm::FlipVertical ? sprite->height() - mask->bounds().y2(): mask->bounds().y)))); document->generateMaskBoundaries(); } transaction.commit(); } update_screen_for_document(document); }
ToolLoopImpl(Editor* editor, Context* context, tools::Tool* tool, tools::Ink* ink, Document* document, tools::ToolLoop::Button button, const app::Color& fgColor, const app::Color& bgColor) : ToolLoopBase(editor, tool, ink, document, button, fgColor, bgColor) , m_context(context) , m_canceled(false) , m_transaction(m_context, m_tool->getText().c_str(), ((getInk()->isSelection() || getInk()->isEyedropper() || getInk()->isScrollMovement() || getInk()->isSlice() || getInk()->isZoom()) ? DoesntModifyDocument: ModifyDocument)) , m_expandCelCanvas(editor->getSite(), m_docPref.tiled.mode(), m_transaction, ExpandCelCanvas::Flags( ExpandCelCanvas::NeedsSource | // If the tool is freehand-like, we can use the modified // region directly as undo information to save the modified // pixels (it's faster than creating a Dirty object). // See ExpandCelCanvas::commit() for details about this flag. (getController()->isFreehand() ? ExpandCelCanvas::UseModifiedRegionAsUndoInfo: ExpandCelCanvas::None))) { ASSERT(m_context->activeDocument() == m_editor->document()); // Settings switch (tool->getFill(m_button)) { case tools::FillNone: m_filled = false; break; case tools::FillAlways: m_filled = true; break; case tools::FillOptional: m_filled = m_toolPref.filled(); break; } m_previewFilled = m_toolPref.filledPreview(); m_sprayWidth = m_toolPref.spray.width(); m_spraySpeed = m_toolPref.spray.speed(); if (m_ink->isSelection()) m_useMask = false; else m_useMask = m_document->isMaskVisible(); // Start with an empty mask if the user is selecting with "default selection mode" if (getInk()->isSelection() && (!m_document->isMaskVisible() || (int(getModifiers()) & int(tools::ToolLoopModifiers::kReplaceSelection)))) { Mask emptyMask; m_transaction.execute(new cmd::SetMask(m_document, &emptyMask)); } m_celOrigin = m_expandCelCanvas.getCel()->position(); m_mask = m_document->mask(); m_maskOrigin = (!m_mask->isEmpty() ? gfx::Point(m_mask->bounds().x-m_celOrigin.x, m_mask->bounds().y-m_celOrigin.y): gfx::Point(0, 0)); }
// [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(); }