void UndoTransaction::pasteImage(const Image* src_image, int x, int y, int opacity)
{
  const Layer* layer = m_sprite->getCurrentLayer();

  ASSERT(layer);
  ASSERT(layer->is_image());
  ASSERT(layer->is_readable());
  ASSERT(layer->is_writable());

  Cel* cel = ((LayerImage*)layer)->getCel(m_sprite->getCurrentFrame());
  ASSERT(cel);

  Image* cel_image = m_sprite->getStock()->getImage(cel->getImage());
  Image* cel_image2 = Image::createCopy(cel_image);
  image_merge(cel_image2, src_image, x-cel->getX(), y-cel->getY(), opacity, BLEND_MODE_NORMAL);

  replaceStockImage(cel->getImage(), cel_image2); // TODO fix this, improve, avoid replacing the whole image
}
/**
 * Moves every frame in @a layer with the offset (@a dx, @a dy).
 */
void UndoTransaction::displaceLayers(Layer* layer, int dx, int dy)
{
  switch (layer->getType()) {

    case GFXOBJ_LAYER_IMAGE: {
      CelIterator it = ((LayerImage*)layer)->getCelBegin();
      CelIterator end = ((LayerImage*)layer)->getCelEnd();
      for (; it != end; ++it) {
        Cel* cel = *it;
        setCelPosition(cel, cel->getX()+dx, cel->getY()+dy);
      }
      break;
    }

    case GFXOBJ_LAYER_FOLDER: {
      LayerIterator it = ((LayerFolder*)layer)->get_layer_begin();
      LayerIterator end = ((LayerFolder*)layer)->get_layer_end();
      for (; it != end; ++it)
        displaceLayers(*it, dx, dy);
      break;
    }

  }
}
  /**
   * [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_document, "Rotate Canvas");

    // 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:
	  undoTransaction.setCelPosition(cel,
					 m_sprite->getWidth() - cel->getX() - image->w,
					 m_sprite->getHeight() - cel->getY() - image->h);
	  break;
	case 90:
	  undoTransaction.setCelPosition(cel, m_sprite->getHeight() - cel->getY() - image->h, cel->getX());
	  break;
	case -90:
	  undoTransaction.setCelPosition(cel, cel->getY(), m_sprite->getWidth() - cel->getX() - image->w);
	  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_new(image->imgtype,
				   m_angle == 180 ? image->w: image->h,
				   m_angle == 180 ? image->h: image->w);
      image_rotate(image, new_image, m_angle);

      undoTransaction.replaceStockImage(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();
      Mask* new_mask = mask_new();
      int x = 0, y = 0;

      switch (m_angle) {
	case 180:
	  x = m_sprite->getWidth() - origMask->x - origMask->w;
	  y = m_sprite->getHeight() - origMask->y - origMask->h;
	  break;
	case 90:
	  x = m_sprite->getHeight() - origMask->y - origMask->h;
	  y = origMask->x;
	  break;
	case -90:
	  x = origMask->y;
	  y = m_sprite->getWidth() - origMask->x - origMask->w;
	  break;
      }

      // create the new rotated mask
      mask_replace(new_mask, x, y,
		   m_angle == 180 ? origMask->w: origMask->h,
		   m_angle == 180 ? origMask->h: origMask->w);
      image_rotate(origMask->bitmap, new_mask->bitmap, m_angle);

      // copy new mask
      undoTransaction.copyToCurrentMask(new_mask);
      mask_free(new_mask);

      // regenerate mask
      m_document->generateMaskBoundaries();
    }

    // change the sprite's size
    if (m_angle != 180)
      undoTransaction.setSpriteSize(m_sprite->getHeight(), m_sprite->getWidth());

    // commit changes
    undoTransaction.commit();
  }
Exemple #5
0
void FlipCommand::onExecute(Context* context)
{
  ContextWriter writer(context);
  Document* document = writer.document();
  Sprite* sprite = writer.sprite();
  DocumentApi api = document->getApi();

  {
    UndoTransaction undoTransaction(writer.context(),
                                    m_flipMask ?
                                    (m_flipType == raster::algorithm::FlipHorizontal ?
                                     "Flip Horizontal":
                                     "Flip Vertical"):
                                    (m_flipType == raster::algorithm::FlipHorizontal ?
                                     "Flip Canvas Horizontal":
                                     "Flip Canvas Vertical"));

    if (m_flipMask) {
      int x, y;
      Image* image = writer.image(&x, &y);
      if (!image)
        return;

      Mask* mask = NULL;
      bool alreadyFlipped = false;

      // This variable will be the area to be flipped inside the image.
      gfx::Rect bounds(image->getBounds());

      // If there is some portion of sprite selected, we flip the
      // selected region only. If the mask isn't visible, we flip the
      // whole image.
      if (document->isMaskVisible()) {
        mask = document->getMask();

        // Intersect the full area of the image with the mask's
        // bounds, so we don't request to flip an area outside the
        // image's bounds.
        bounds = bounds.createIntersect(gfx::Rect(mask->getBounds()).offset(-x, -y));

        // If the mask isn't a rectangular area, we've to flip the mask too.
        if (mask->getBitmap() != NULL && !mask->isRectangular()) {
          int bgcolor = app_get_color_to_clear_layer(writer.layer());

          // Flip the portion of image specified by the mask.
          mask->offsetOrigin(-x, -y);
          api.flipImageWithMask(image, mask, m_flipType, bgcolor);
          mask->offsetOrigin(x, y);
          alreadyFlipped = true;

          // Flip the mask.
          Image* maskBitmap = mask->getBitmap();
          if (maskBitmap != NULL) {
            // Create a flipped copy of the current mask.
            base::UniquePtr<Mask> newMask(new Mask(*mask));
            newMask->freeze();
            raster::algorithm::flip_image(newMask->getBitmap(),
              maskBitmap->getBounds(), m_flipType);
            newMask->unfreeze();

            // Change the current mask and generate the new boundaries.
            api.copyToCurrentMask(newMask);

            document->generateMaskBoundaries();
          }
        }
      }

      // Flip the portion of image specified by "bounds" variable.
      if (!alreadyFlipped) {
        api.flipImage(image, bounds, m_flipType);
      }
    }
    else {
      // get all sprite cels
      CelList cels;
      sprite->getCels(cels);

      // for each cel...
      for (CelIterator it = cels.begin(); it != cels.end(); ++it) {
        Cel* cel = *it;
        Image* image = sprite->getStock()->getImage(cel->getImage());

        api.setCelPosition
          (sprite, cel,
           (m_flipType == raster::algorithm::FlipHorizontal ?
            sprite->getWidth() - image->getWidth() - cel->getX():
            cel->getX()),
           (m_flipType == raster::algorithm::FlipVertical ?
            sprite->getHeight() - image->getHeight() - cel->getY():
            cel->getY()));

        api.flipImage(image, image->getBounds(), m_flipType);
      }
    }

    undoTransaction.commit();
  }

  update_screen_for_document(document);
}
Exemple #6
0
// Gives to the user the possibility to move the sprite's layer in the
// current editor, returns true if the position was changed.
int interactive_move_layer(int mode, bool use_undo, int (*callback)())
{
    Editor* editor = current_editor;
    Document* document = editor->getDocument();
    undo::UndoHistory* undo = document->getUndoHistory();
    Sprite* sprite = document->getSprite();

    ASSERT(sprite->getCurrentLayer()->is_image());

    LayerImage* layer = static_cast<LayerImage*>(sprite->getCurrentLayer());
    Cel *cel = layer->getCel(sprite->getCurrentFrame());
    int start_x, new_x;
    int start_y, new_y;
    int start_b;
    int ret;
    int update = false;
    int quiet_clock = -1;
    int first_time = true;
    int begin_x;
    int begin_y;

    if (!cel)
        return false;

    begin_x = cel->getX();
    begin_y = cel->getY();

    editor->hideDrawingCursor();
    jmouse_set_cursor(JI_CURSOR_MOVE);

    editor->editor_click_start(mode, &start_x, &start_y, &start_b);

    do {
        if (update) {
            cel->setPosition(begin_x - start_x + new_x,
                             begin_y - start_y + new_y);

            // Update layer-bounds.
            editor->invalidate();

            // Update status bar.
            app_get_statusbar()->setStatusText
            (0,
             "Pos %3d %3d Offset %3d %3d",
             (int)cel->getX(),
             (int)cel->getY(),
             (int)(cel->getX() - begin_x),
             (int)(cel->getY() - begin_y));

            /* update clock */
            quiet_clock = ji_clock;
            first_time = false;
        }

        /* call the user's routine */
        if (callback)
            (*callback)();

        /* redraw dirty widgets */
        jwidget_flush_redraw(ji_get_default_manager());
        jmanager_dispatch_messages(ji_get_default_manager());

        gui_feedback();
    } while (editor->editor_click(&new_x, &new_y, &update, NULL));

    new_x = cel->getX();
    new_y = cel->getY();
    cel->setPosition(begin_x, begin_y);

    /* the position was changed */
    if (!editor->editor_click_cancel()) {
        if (use_undo && undo->isEnabled()) {
            undo->setLabel("Cel Movement");
            undo->setModification(undo::ModifyDocument);

            undo->pushUndoer(new undoers::SetCelPosition(undo->getObjects(), cel));
        }

        cel->setPosition(new_x, new_y);
        ret = true;
    }
    /* the position wasn't changed */
    else {
        ret = false;
    }

    /* redraw the sprite in all editors */
    update_screen_for_document(document);

    /* restore the cursor */
    editor->showDrawingCursor();

    editor->editor_click_done();

    return ret;
}
// clears the mask region in the current sprite with the specified background color
void UndoTransaction::clearMask(int bgcolor)
{
  Cel* cel = getCurrentCel();
  if (!cel)
    return;

  Image* image = getCelImage(cel);
  if (!image)
    return;

  Mask* mask = m_document->getMask();

  // If the mask is empty or is not visible then we have to clear the
  // entire image in the cel.
  if (!m_document->isMaskVisible()) {
    // If the layer is the background then we clear the image.
    if (m_sprite->getCurrentLayer()->is_background()) {
      if (isEnabled())
        m_undoHistory->pushUndoer(new undoers::ImageArea(m_undoHistory->getObjects(),
            image, 0, 0, image->w, image->h));

      // clear all
      image_clear(image, bgcolor);
    }
    // If the layer is transparent we can remove the cel (and its
    // associated image).
    else {
      removeCel(static_cast<LayerImage*>(m_sprite->getCurrentLayer()), cel);
    }
  }
  else {
    int offset_x = mask->getBounds().x-cel->getX();
    int offset_y = mask->getBounds().y-cel->getY();
    int u, v, putx, puty;
    int x1 = MAX(0, offset_x);
    int y1 = MAX(0, offset_y);
    int x2 = MIN(image->w-1, offset_x+mask->getBounds().w-1);
    int y2 = MIN(image->h-1, offset_y+mask->getBounds().h-1);

    // do nothing
    if (x1 > x2 || y1 > y2)
      return;

    if (isEnabled())
      m_undoHistory->pushUndoer(new undoers::ImageArea(m_undoHistory->getObjects(),
          image, x1, y1, x2-x1+1, y2-y1+1));

    // clear the masked zones
    for (v=0; v<mask->getBounds().h; v++) {
      div_t d = div(0, 8);
      uint8_t* address = ((uint8_t**)mask->getBitmap()->line)[v]+d.quot;

      for (u=0; u<mask->getBounds().w; u++) {
        if ((*address & (1<<d.rem))) {
          putx = u + offset_x;
          puty = v + offset_y;
          image_putpixel(image, putx, puty, bgcolor);
        }

        _image_bitmap_next_bit(d, address);
      }
    }
  }
}
void UndoTransaction::backgroundFromLayer(LayerImage* layer, int bgcolor)
{
  ASSERT(layer);
  ASSERT(layer->is_image());
  ASSERT(layer->is_readable());
  ASSERT(layer->is_writable());
  ASSERT(layer->getSprite() == m_sprite);
  ASSERT(m_sprite->getBackgroundLayer() == NULL);

  // create a temporary image to draw each frame of the new
  // `Background' layer
  UniquePtr<Image> bg_image_wrap(Image::create(m_sprite->getPixelFormat(),
                                               m_sprite->getWidth(),
                                               m_sprite->getHeight()));
  Image* bg_image = bg_image_wrap.get();

  CelIterator it = layer->getCelBegin();
  CelIterator end = layer->getCelEnd();

  for (; it != end; ++it) {
    Cel* cel = *it;
    ASSERT((cel->getImage() > 0) &&
           (cel->getImage() < m_sprite->getStock()->size()));

    // get the image from the sprite's stock of images
    Image* cel_image = m_sprite->getStock()->getImage(cel->getImage());
    ASSERT(cel_image);

    image_clear(bg_image, bgcolor);
    image_merge(bg_image, cel_image,
                cel->getX(),
                cel->getY(),
                MID(0, cel->getOpacity(), 255),
                layer->getBlendMode());

    // now we have to copy the new image (bg_image) to the cel...
    setCelPosition(cel, 0, 0);

    // same size of cel-image and bg-image
    if (bg_image->w == cel_image->w &&
        bg_image->h == cel_image->h) {
      if (isEnabled())
        m_undoHistory->pushUndoer(new undoers::ImageArea(m_undoHistory->getObjects(),
            cel_image, 0, 0, cel_image->w, cel_image->h));

      image_copy(cel_image, bg_image, 0, 0);
    }
    else {
      replaceStockImage(cel->getImage(), Image::createCopy(bg_image));
    }
  }

  // Fill all empty cels with a flat-image filled with bgcolor
  for (int frame=0; frame<m_sprite->getTotalFrames(); frame++) {
    Cel* cel = layer->getCel(frame);
    if (!cel) {
      Image* cel_image = Image::create(m_sprite->getPixelFormat(), m_sprite->getWidth(), m_sprite->getHeight());
      image_clear(cel_image, bgcolor);

      // Add the new image in the stock
      int image_index = addImageInStock(cel_image);

      // Create the new cel and add it to the new background layer
      cel = new Cel(frame, image_index);
      addCel(layer, cel);
    }
  }

  configureLayerAsBackground(layer);
}