Esempio n. 1
0
TEST(LayerList, AreLayersAdjacent)
{
  std::unique_ptr<Sprite> spr(new Sprite(IMAGE_RGB, 32, 32, 256));
  LayerGroup* root = spr->root();
  Layer* layer1 = new LayerImage(spr.get());
  Layer* layer2 = new LayerImage(spr.get());
  Layer* layer3 = new LayerImage(spr.get());
  Layer* layer4 = new LayerImage(spr.get());
  root->addLayer(layer1);
  root->addLayer(layer2);
  root->addLayer(layer3);
  root->addLayer(layer4);

  LayerList layers;
  root->allLayers(layers);
  EXPECT_EQ(4, layers.size());
  EXPECT_TRUE(are_layers_adjacent(layers));

  std::reverse(layers.begin(), layers.end());
  EXPECT_EQ(4, layers.size());
  EXPECT_TRUE(are_layers_adjacent(layers));

  layers.erase(layers.begin());
  EXPECT_EQ(3, layers.size());
  EXPECT_TRUE(are_layers_adjacent(layers));

  layers.erase(layers.begin()+1);
  EXPECT_EQ(2, layers.size());
  EXPECT_FALSE(are_layers_adjacent(layers));

  std::reverse(layers.begin(), layers.end());
  EXPECT_EQ(2, layers.size());
  EXPECT_FALSE(are_layers_adjacent(layers));

  layers.erase(layers.begin());
  EXPECT_EQ(1, layers.size());
  EXPECT_TRUE(are_layers_adjacent(layers));
}
Esempio n. 2
0
void Controller::DispatchUpdate(DeltaTime &delta)
{
	if (!mScene) {
		return;
	}

	LayerList *layers = &mScene->GetLayers();
	LayerIter liter = layers->begin();

	mScene->Update(delta);

	while (liter != layers->end()) {
		(*liter)->UpdateSelfAndChildren(delta);
		liter++;
	}
}
Esempio n. 3
0
// Reports whether the passed in layers have visible damage or are otherwise
// dirty because render properties changed.
// Assumes that layers in the list belong to same composition. ie. damage to
// one layer affects the others.  A warning is logged if the assumption is wrong.
bool GLXGraphicsystem::needsRedraw(LayerList layers)
{
    // TODO: Ignore damage from completely obscured surfaces

    for (LayerListConstIterator layer = layers.begin(); layer != layers.end(); layer++)
    {
        if ((*layer)->getLayerType() == Hardware && layers.size() > 1)
        {
            // Damage in a hardware layer should not imply a redraw in other layers
            LOG_WARNING("GLXGraphicsystem", "needsRedraw() called with layers not in the same composition");
        }

        if (needsRedraw(*layer))
        {
            return true;
        }
    }
    return false;
}
Esempio n. 4
0
void GLXGraphicsystem::renderSWLayers(LayerList layers, bool clear)
{
    // This is a stub.
    //
    // TODO: render in a more optimal way
    //   1. Turn off blending for first surface rendered
    //   2. Don't clear when it's legal to avoid it
    //         eg. a fullscreen opaque surface exists
    //   3. Render multiple surfaces at time via multi-texturing
    //   4. Remove fully obscured layers/surfaces
    if (clear)
    {
        clearBackground();
    }

    for (LayerListConstIterator layer = layers.begin(); layer != layers.end(); layer++)
    {
        renderSWLayer(*layer, false); // Don't clear
    }
}
Esempio n. 5
0
static DocRange drop_range_op(
  Doc* doc,
  const Op op,
  const DocRange& from,
  DocRangePlace place,
  const TagsHandling tagsHandling,
  DocRange to)
{
  // Convert "first child" operation into a insert after last child.
  LayerGroup* parent = nullptr;
  if (to.type() == DocRange::kLayers &&
      !to.selectedLayers().empty()) {
    if (place == kDocRangeFirstChild &&
        (*to.selectedLayers().begin())->isGroup()) {
      place = kDocRangeAfter;
      parent = static_cast<LayerGroup*>((*to.selectedLayers().begin()));

      to.clearRange();
      to.startRange(parent->lastLayer(), -1, DocRange::kLayers);
      to.endRange(parent->lastLayer(), -1);
    }
    else {
      parent = (*to.selectedLayers().begin())->parent();
    }

    // Check that we're not moving a group inside itself
    for (auto moveThis : from.selectedLayers()) {
      if (moveThis == parent ||
          (moveThis->isGroup() &&
           has_child(static_cast<LayerGroup*>(moveThis), parent)))
        return from;
    }
  }

  if (place != kDocRangeBefore &&
      place != kDocRangeAfter) {
    ASSERT(false);
    throw std::invalid_argument("Invalid 'place' argument");
  }

  Sprite* sprite = doc->sprite();

  // Check noop/trivial/do nothing cases, i.e., move a range to the same place.
  // Also check invalid cases, like moving a Background layer.
  switch (from.type()) {

    case DocRange::kCels:
      if (from == to)
        return from;
      break;

    case DocRange::kFrames:
      if (op == Move) {
        // Simple cases with one continuos range of frames that are a
        // no-op.
        if ((from.selectedFrames().ranges() == 1) &&
            ((to.firstFrame() >= from.firstFrame() &&
              to.lastFrame() <= from.lastFrame()) ||
             (place == kDocRangeBefore && to.firstFrame() == from.lastFrame()+1) ||
             (place == kDocRangeAfter && to.lastFrame() == from.firstFrame()-1)) &&
            // If there are tags, this might not be a no-op
            (sprite->frameTags().empty() ||
             tagsHandling == kDontAdjustTags)) {
          return from;
        }
      }
      break;

    case DocRange::kLayers:
      if (op == Move) {
        SelectedLayers srcSelLayers = from.selectedLayers();
        SelectedLayers dstSelLayers = to.selectedLayers();
        LayerList srcLayers = srcSelLayers.toLayerList();
        LayerList dstLayers = dstSelLayers.toLayerList();
        ASSERT(!srcLayers.empty());
        if (srcLayers.empty())
          return from;

        // dstLayers can be nullptr when we insert the first child in
        // a group.

        // Check no-ops when we move layers at the same level (all
        // layers with the same parent), all adjacents, and which are
        // moved to the same place.
        if (!dstSelLayers.empty() &&
            srcSelLayers.hasSameParent() &&
            dstSelLayers.hasSameParent() &&
            are_layers_adjacent(srcLayers) &&
            are_layers_adjacent(dstLayers)) {
          for (Layer* srcLayer : srcLayers)
            if (dstSelLayers.contains(srcLayer))
              return from;

          if ((place == kDocRangeBefore
               && dstLayers.front() == srcLayers.back()->getNext()) ||
              (place == kDocRangeAfter
               && dstLayers.back() == srcLayers.front()->getPrevious()))
            return from;
        }

        // We cannot move the background
        for (Layer* layer : srcSelLayers)
          if (layer->isBackground())
            throw std::runtime_error("The background layer cannot be moved");
      }

      // Before background
      if (place == kDocRangeBefore) {
        for (Layer* background : to.selectedLayers()) {
          if (background && background->isBackground())
            throw std::runtime_error("You cannot move or copy something below the background layer");
        }
      }
      break;
  }

  const char* undoLabel = NULL;
  switch (op) {
    case Move: undoLabel = "Move Range"; break;
    case Copy: undoLabel = "Copy Range"; break;
    default:
      ASSERT(false);
      throw std::invalid_argument("Invalid 'op' argument");
  }
  DocRange resultRange;

  {
    const app::Context* context = static_cast<app::Context*>(doc->context());
    const ContextReader reader(context);
    ContextWriter writer(reader, 500);
    Transaction transaction(writer.context(), undoLabel, ModifyDocument);
    DocApi api = doc->getApi(transaction);

    // TODO Try to add the range with just one call to DocApi
    // methods, to avoid generating a lot of cmd::SetCelFrame (see
    // DocApi::setCelFramePosition() function).

    switch (from.type()) {

      case DocRange::kCels: {
        LayerList allLayers = sprite->allBrowsableLayers();
        if (allLayers.empty())
          break;

        LayerList srcLayers = from.selectedLayers().toLayerList();
        LayerList dstLayers = to.selectedLayers().toLayerList();
        if (srcLayers.empty() ||
            dstLayers.empty())
          throw std::invalid_argument("You need to specify a non-empty cels range");

        if (find_layer_index(allLayers, srcLayers.front()) <
            find_layer_index(allLayers, dstLayers.front())) {
          std::reverse(srcLayers.begin(), srcLayers.end());
          std::reverse(dstLayers.begin(), dstLayers.end());
        }

        if (from.firstFrame() < to.firstFrame()) {
          auto srcFrames = from.selectedFrames().makeReverse();
          auto dstFrames = to.selectedFrames().makeReverse();

          move_or_copy_cels(api, op, srcLayers, dstLayers, srcFrames, dstFrames);
        }
        else {
          const auto& srcFrames = from.selectedFrames();
          const auto& dstFrames = to.selectedFrames();

          move_or_copy_cels(api, op, srcLayers, dstLayers, srcFrames, dstFrames);
        }

        resultRange = to;
        break;
      }

      case DocRange::kFrames: {
        frame_t dstFrame;
        if (place == kDocRangeBefore)
          dstFrame = to.firstFrame();
        else
          dstFrame = to.lastFrame();

        resultRange = move_or_copy_frames(api, op, sprite,
                                          from, dstFrame,
                                          place, tagsHandling);
        break;
      }

      case DocRange::kLayers: {
        LayerList allLayers = sprite->allBrowsableLayers();
        if (allLayers.empty())
          break;

        LayerList srcLayers = from.selectedLayers().toLayerList();
        LayerList dstLayers = to.selectedLayers().toLayerList();
        ASSERT(!srcLayers.empty());

        switch (op) {

          case Move:
            if (place == kDocRangeBefore) {
              Layer* beforeThis = (!dstLayers.empty() ? dstLayers.front(): nullptr);
              Layer* afterThis  = nullptr;

              for (Layer* srcLayer : srcLayers) {
                if (afterThis)
                  api.restackLayerAfter(srcLayer, parent, afterThis);
                else
                  api.restackLayerBefore(srcLayer, parent, beforeThis);

                afterThis = srcLayer;
              }
            }
            else if (place == kDocRangeAfter) {
              Layer* afterThis = (!dstLayers.empty() ? dstLayers.back(): nullptr);
              for (Layer* srcLayer : srcLayers) {
                api.restackLayerAfter(srcLayer, parent, afterThis);
                afterThis = srcLayer;
              }
            }

            // Same set of layers than the "from" range
            resultRange = from;
            break;

          case Copy: {
            if (place == kDocRangeBefore) {
              Layer* beforeThis = (!dstLayers.empty() ? dstLayers.front(): nullptr);
              for (Layer* srcLayer :  srcLayers) {
                Layer* copiedLayer = api.duplicateLayerBefore(
                  srcLayer, parent, beforeThis);

                resultRange.startRange(copiedLayer, -1, DocRange::kLayers);
                resultRange.endRange(copiedLayer, -1);
              }
            }
            else if (place == kDocRangeAfter) {
              std::reverse(srcLayers.begin(), srcLayers.end());

              Layer* afterThis = (!dstLayers.empty() ? dstLayers.back(): nullptr);
              for (Layer* srcLayer :  srcLayers) {
                Layer* copiedLayer = api.duplicateLayerAfter(
                  srcLayer, parent, afterThis);

                resultRange.startRange(copiedLayer, -1, DocRange::kLayers);
                resultRange.endRange(copiedLayer, -1);
              }
            }
            break;
          }
        }
        break;
      }
    }

    if (resultRange.type() != DocRange::kNone)
      transaction.setNewDocRange(resultRange);

    transaction.commit();
  }

  return resultRange;
}
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);
}
  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);
  }
Esempio n. 8
0
void UndoTransaction::flattenLayers(int bgcolor)
{
  Image* cel_image;
  Cel* cel;
  int frame;

  // create a temporary image
  UniquePtr<Image> image_wrap(Image::create(m_sprite->getPixelFormat(),
                                            m_sprite->getWidth(),
                                            m_sprite->getHeight()));
  Image* image = image_wrap.get();

  /* get the background layer from the sprite */
  LayerImage* background = m_sprite->getBackgroundLayer();
  if (!background) {
    /* if there aren't a background layer we must to create the background */
    background = new LayerImage(m_sprite);

    if (isEnabled())
      m_undoHistory->pushUndoer(new undoers::AddLayer(m_undoHistory->getObjects(),
          m_sprite->getFolder(), background));

    m_sprite->getFolder()->add_layer(background);

    if (isEnabled())
      m_undoHistory->pushUndoer(new undoers::MoveLayer(m_undoHistory->getObjects(),
          background));

    background->configureAsBackground();
  }

  /* copy all frames to the background */
  for (frame=0; frame<m_sprite->getTotalFrames(); frame++) {
    /* clear the image and render this frame */
    image_clear(image, bgcolor);
    layer_render(m_sprite->getFolder(), image, 0, 0, frame);

    cel = background->getCel(frame);
    if (cel) {
      cel_image = m_sprite->getStock()->getImage(cel->getImage());
      ASSERT(cel_image != NULL);

      /* we have to save the current state of `cel_image' in the undo */
      if (isEnabled()) {
        Dirty* dirty = new Dirty(cel_image, image);
        dirty->saveImagePixels(cel_image);
        m_undoHistory->pushUndoer(new undoers::DirtyArea(
            m_undoHistory->getObjects(), cel_image, dirty));
        delete dirty;
      }
    }
    else {
      /* if there aren't a cel in this frame in the background, we
         have to create a copy of the image for the new cel */
      cel_image = Image::createCopy(image);
      /* TODO error handling: if (!cel_image) { ... } */

      /* here we create the new cel (with the new image `cel_image') */
      cel = new Cel(frame, m_sprite->getStock()->addImage(cel_image));
      /* TODO error handling: if (!cel) { ... } */

      /* and finally we add the cel in the background */
      background->addCel(cel);
    }

    image_copy(cel_image, image, 0, 0);
  }

  /* select the background */
  if (m_sprite->getCurrentLayer() != background) {
    if (isEnabled())
      m_undoHistory->pushUndoer(new undoers::SetCurrentLayer(
          m_undoHistory->getObjects(), m_sprite));

    m_sprite->setCurrentLayer(background);
  }

  // Remove old layers.
  LayerList layers = m_sprite->getFolder()->get_layers_list();
  LayerIterator it = layers.begin();
  LayerIterator end = layers.end();

  for (; it != end; ++it) {
    if (*it != background) {
      Layer* old_layer = *it;

      // Remove the layer
      if (isEnabled())
        m_undoHistory->pushUndoer(new undoers::RemoveLayer(m_undoHistory->getObjects(),
            old_layer));

      m_sprite->getFolder()->remove_layer(old_layer);

      // Destroy the layer
      delete old_layer;
    }
  }
}
Esempio n. 9
0
DocDiff compare_docs(const Doc* a,
                     const Doc* b)
{
  DocDiff diff;

  // Don't compare filenames
  //if (a->filename() != b->filename())...

  // Compare sprite specs
  if (a->sprite()->width() != b->sprite()->width() ||
      a->sprite()->height() != b->sprite()->height() ||
      a->sprite()->pixelFormat() != b->sprite()->pixelFormat()) {
    diff.anything = diff.canvas = true;
  }

  // Frames layers
  if (a->sprite()->totalFrames() != b->sprite()->totalFrames()) {
    diff.anything = diff.totalFrames = true;
  }
  else {
    for (frame_t f=0; f<a->sprite()->totalFrames(); ++f) {
      if (a->sprite()->frameDuration(f) != b->sprite()->frameDuration(f)) {
        diff.anything = diff.frameDuration = true;
        break;
      }
    }
  }

  // Tags
  if (a->sprite()->frameTags().size() != b->sprite()->frameTags().size()) {
    diff.anything = diff.frameTags = true;
  }
  else {
    auto aIt = a->sprite()->frameTags().begin(), aEnd = a->sprite()->frameTags().end();
    auto bIt = b->sprite()->frameTags().begin(), bEnd = b->sprite()->frameTags().end();
    for (; aIt != aEnd && bIt != bEnd; ++aIt, ++bIt) {
      const FrameTag* aTag = *aIt;
      const FrameTag* bTag = *bIt;
      if (aTag->fromFrame() != bTag->fromFrame() ||
          aTag->toFrame()   != bTag->toFrame()   ||
          aTag->name()      != bTag->name()      ||
          aTag->color()     != bTag->color()     ||
          aTag->aniDir()    != bTag->aniDir()) {
        diff.anything = diff.frameTags = true;
      }
    }
  }

  // Palettes layers
  if (a->sprite()->getPalettes().size() != b->sprite()->getPalettes().size()) {
    const PalettesList& aPals = a->sprite()->getPalettes();
    const PalettesList& bPals = b->sprite()->getPalettes();
    auto aIt = aPals.begin(), aEnd = aPals.end();
    auto bIt = bPals.begin(), bEnd = bPals.end();

    for (; aIt != aEnd && bIt != bEnd; ++aIt, ++bIt) {
      const Palette* aPal = *aIt;
      const Palette* bPal = *bIt;

      if (aPal->countDiff(bPal, nullptr, nullptr)) {
        diff.anything = diff.palettes = true;
        break;
      }
    }
  }

  // Compare layers
  if (a->sprite()->allLayersCount() != b->sprite()->allLayersCount()) {
    diff.anything = diff.layers = true;
  }
  else {
    LayerList aLayers = a->sprite()->allLayers();
    LayerList bLayers = b->sprite()->allLayers();
    auto aIt = aLayers.begin(), aEnd = aLayers.end();
    auto bIt = bLayers.begin(), bEnd = bLayers.end();

    for (; aIt != aEnd && bIt != bEnd; ++aIt, ++bIt) {
      const Layer* aLay = *aIt;
      const Layer* bLay = *bIt;

      if (aLay->type() != bLay->type() ||
          aLay->name() != bLay->name() ||
          aLay->flags() != bLay->flags() ||
          (aLay->isImage() && bLay->isImage() &&
           (((const LayerImage*)aLay)->opacity() != ((const LayerImage*)bLay)->opacity()))) {
        diff.anything = diff.layers = true;
        break;
      }

      if (diff.totalFrames) {
        for (frame_t f=0; f<a->sprite()->totalFrames(); ++f) {
          const Cel* aCel = aLay->cel(f);
          const Cel* bCel = bLay->cel(f);

          if ((!aCel && bCel) ||
              (aCel && !bCel)) {
            diff.anything = diff.cels = true;
          }
          else if (aCel && bCel) {
            if (aCel->frame() == bCel->frame() ||
                aCel->bounds() == bCel->bounds() ||
                aCel->opacity() == bCel->opacity()) {
              diff.anything = diff.cels = true;
            }
            if (aCel->image() && bCel->image()) {
              if (aCel->image()->bounds() != bCel->image()->bounds() ||
                  count_diff_between_images(aCel->image(), bCel->image()))
                diff.anything = diff.images = true;
            }
            else if (aCel->image() != bCel->image())
              diff.anything = diff.images = true;
          }
        }
      }
    }
  }

  // Compare color spaces
  if (!a->sprite()->colorSpace()->nearlyEqual(*b->sprite()->colorSpace())) {
    diff.anything = diff.colorProfiles = true;
  }

  return diff;
}
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);
}
Esempio n. 11
0
void DocumentApi::flattenLayers(Sprite* sprite, int bgcolor)
{
  Image* cel_image;
  Cel* cel;

  DocumentUndo* undo = m_document->getUndo();

  // Create a temporary image.
  UniquePtr<Image> image_wrap(Image::create(sprite->getPixelFormat(),
                                            sprite->getWidth(),
                                            sprite->getHeight()));
  Image* image = image_wrap.get();

  // Get the background layer from the sprite.
  LayerImage* background = sprite->getBackgroundLayer();
  if (!background) {
    // If there aren't a background layer we must to create the background.
    background = new LayerImage(sprite);

    addLayer(sprite->getFolder(), background, NULL);
    configureLayerAsBackground(background);
  }

  // Copy all frames to the background.
  for (FrameNumber frame(0); frame<sprite->getTotalFrames(); ++frame) {
    // Clear the image and render this frame.
    image_clear(image, bgcolor);
    layer_render(sprite->getFolder(), image, 0, 0, frame);

    cel = background->getCel(frame);
    if (cel) {
      cel_image = sprite->getStock()->getImage(cel->getImage());
      ASSERT(cel_image != NULL);

      // We have to save the current state of `cel_image' in the undo.
      if (undo->isEnabled()) {
        Dirty* dirty = new Dirty(cel_image, image);
        dirty->saveImagePixels(cel_image);
        m_undoers->pushUndoer(new undoers::DirtyArea(
            getObjects(), cel_image, dirty));
        delete dirty;
      }
    }
    else {
      // If there aren't a cel in this frame in the background, we
      // have to create a copy of the image for the new cel.
      cel_image = Image::createCopy(image);
      // TODO error handling: if createCopy throws

      // Here we create the new cel (with the new image `cel_image').
      cel = new Cel(frame, sprite->getStock()->addImage(cel_image));
      // TODO error handling: if new Cel throws

      // And finally we add the cel in the background.
      background->addCel(cel);
    }

    image_copy(cel_image, image, 0, 0);
  }

  // Delete old layers.
  LayerList layers = sprite->getFolder()->getLayersList();
  LayerIterator it = layers.begin();
  LayerIterator end = layers.end();
  for (; it != end; ++it)
    if (*it != background)
      removeLayer(*it);
}