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 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); } }
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 ShiftMaskedCel::shift(int dx, int dy) { Cel* cel = this->cel(); Image* image = cel->image(); Mask* mask = static_cast<app::Document*>(cel->document())->mask(); ASSERT(mask->bitmap()); if (!mask->bitmap()) return; int x = cel->x(); int y = cel->y(); mask->offsetOrigin(-x, -y); doc::algorithm::shift_image_with_mask(image, mask, dx, dy); mask->offsetOrigin(x, y); image->incrementVersion(); }
// Loads a MSK file (Animator and Animator Pro format) Mask* load_msk_file(const char* filename) { int orig_size = base::file_size(filename); int i, c, u, v, byte, magic, size; Mask* mask = NULL; FILE* f = base::open_file_raw(filename, "r"); if (!f) return NULL; size = base::fgetl(f); magic = base::fgetw(f); // Animator Pro MSK format if ((size == orig_size) && (magic == 0x9500)) { int x, y; fclose(f); // Just load an Animator Pro PIC file base::UniquePtr<Image> image(load_pic_file(filename, &x, &y, NULL)); if (image != NULL && (image->pixelFormat() == IMAGE_BITMAP)) mask = new Mask(x, y, image.release()); } // Animator MSK format else if (orig_size == 8000) { mask = new Mask(); mask->replace(0, 0, 320, 200); u = v = 0; for (i=0; i<8000; i++) { byte = getc(f); for (c=0; c<8; c++) { mask->bitmap()->putPixel(u, v, byte & (1<<(7-c))); u++; if (u == 320) { u = 0; v++; } } } fclose(f); } else { fclose(f); } return mask; }
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); }
// [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(); }