Delphi graphics32将图层保存为透明PNG出错

Delphi graphics32将图层保存为透明PNG出错,delphi,graphics32,Delphi,Graphics32,我有一个奇怪的问题,我想我解决不了。 我有一个包含层(透明png图像)的ImgView,我打算将所有层保存为png文件(就像“保存项目”一样),以便以后我可以重新打开它们并将它们放在我留下的地方。(就像“开放项目”一样) 这是我的问题,以下步骤效果很好: 我添加图层(透明PNG文件) 我把它们四处移动,放在我想要的地方 我按save project(因此这里我将所有图层保存为png图像文件) 它起作用了 如果我执行以下步骤,则会出现问题: 我添加图层(透明PNG文件) 我把它们四处移动,放在我想

我有一个奇怪的问题,我想我解决不了。 我有一个包含层(透明png图像)的ImgView,我打算将所有层保存为png文件(就像“保存项目”一样),以便以后我可以重新打开它们并将它们放在我留下的地方。(就像“开放项目”一样) 这是我的问题,以下步骤效果很好:

  • 我添加图层(透明PNG文件)
  • 我把它们四处移动,放在我想要的地方
  • 我按save project(因此这里我将所有图层保存为png图像文件)
  • 它起作用了
  • 如果我执行以下步骤,则会出现问题:

  • 我添加图层(透明PNG文件)
  • 我把它们四处移动,放在我想要的地方
  • 我更改层的位置(例如:发送回一个层)(因此此步骤不同)
  • 我按save project(因此这里我将所有图层保存为png图像文件)
  • 它因“模块'MyApp.exe'中地址005380FB的访问冲突而崩溃。读取地址000000 C0”
  • 现在它只给了我以上的错误,但是在几次运行之前,它指出了我的这一行:

    procedure TCustomBitmap32.ResetAlpha(const AlphaValue: Byte);
    var
      I: Integer;
      P: PByteArray;
    begin
      if not FMeasuringMode then  <<<<<------ this line
    
    。。。及

    procedure SaveBmpAsPng(bmp:TBitmap32;dest:string);
    var
      Y: Integer;
      X: Integer;
      Png: TPortableNetworkGraphic32;
    
      function IsWhite(Color32: TColor32): Boolean;
      begin
        Result:= (TColor32Entry(Color32).B = 255) and
                 (TColor32Entry(Color32).G = 255) and
                 (TColor32Entry(Color32).R = 255);
      end;
    
    begin
        bmp.ResetAlpha;
        for Y := 0 to bmp.Height-1 do
          for X := 0 to bmp.Width-1 do
          begin
            if IsWhite(bmp.Pixel[X, Y]) then
              bmp.Pixel[X,Y]:=Color32(255,255,255,0);
          end;
        Png:= TPortableNetworkGraphic32.Create;
        Png.Assign(bmp);
        Png.SaveToFile(dest);
        Png.Free;
    end;
    
    有什么不对劲吗? 请帮忙

    编辑 我想我发现了我的问题。。。 当我移动图层时,唯一的方法(据我所知)是将所有图层加载到imagelist(TBitmap32List是我当时的选择)中,然后清理图层,并按所需顺序将其从imagelist重新添加到ImageView中。 我只能假设这是出问题的地方。 这一定是因为在层中我有透明的PNG,当我将它们加载到Bitmap32List中时,我将它们作为BMP加载。 在继续之前,我必须寻找另一种方法来重新组织我的层。我将用我的解决方案更新您。如果你们中有人知道在ImageView32中重新排列图层的更好方法,请告诉我

    编辑

    因此,请在下图中观察GUI已完成,并且正在工作。我有代表层的面板,我可以移动它们(正如您在图片中看到的,我正在拖动层'Elementul 0'并在链中向上移动)。 我重复一遍,当我使用临时文件按顺序上下移动图层时,我的逻辑也起作用。其中一个答案建议我应该只使用Index属性来更改层层次结构中的层位置,我的意思是,如果不至少向图像添加新层,就无法完成此操作。所以这不是一个双重问题。这只是对我收到的一个答案的回应


    谢谢

    通过您对如何更改图层顺序的描述,这很可能是您出现问题的原因。由于您没有发布该部分代码,因此无法确定对其进行评估


    无论如何,要重新排列层,您可以使用
    TCustomLayer
    Index
    属性(其中
    TBitmapLayer
    是其后代)

    因此,在重新排列层时,问题的解决方案是不使用位图32列表作为png层的临时容器,因为在这个过程中会丢失一些内容。 因此,尝试其他重新排序的解决方案。我目前的解决方案是将层作为PNG文件放到磁盘上,然后按所需顺序从磁盘重新加载它们。 另一个解决方案(尚未测试)是创建与现有层数量相等的新层,将实际层移到那里,然后按所需顺序逐个取回,然后移除额外的层


    无论如何。这就是问题所在,这是迄今为止的答案。你的问题比你想象的要简单得多。使用图层很自然:

    发送回

    将层的索引设置为0或只需调用
    SendToBack
    。在此之前的所有层的索引都将增加1。之前在它之后的所有层都保持在相同的位置

    向后发送

    将层的索引减少1。之前的图层现在位于它之后,因此其索引增加了一

    向前发送

    将层的索引增加1。之前位于它后面的图层现在位于它前面,因此其索引减少了一

    发送到前端

    将层的索引设置为层数减1。之前在它之后的图层增加了一层,减少了一层

    因此,绝对不需要触摸位图、将其保存到磁盘或使用任何类型的临时层来更改顺序。实际上,在任何情况下,只要将层的索引设置为您希望它出现的位置(从0开始,从后到前计数),就会发生正确的事情。移动列表中的面板后,可以将相应图层的索引设置为列表中面板的新索引。但是,由于面板是前后顺序排列的,GR32是前后顺序排列的,因此需要将面板的索引转换为所需的层索引

    下面是一个使用
    t列表框
    t按钮
    执行此操作的示例:

    procedure TForm1.SendBackwardButtonClick(Sender: TObject);
    var
      LNewListBoxItemIndex: Integer;
    begin
      // Calculate the new list index and make sure it's valid
      LNewListBoxItemIndex := Max(0, Min(ListBox1.ItemIndex + 1, ListBox1.Items.Count - 1));
      // Transform the current and new list indices and use them to move the layer
      ImgView321.Layers[ListBox1.Items.Count - 1 - ListBox1.ItemIndex].Index :=
        ListBox1.Items.Count - 1 - LNewListBoxItemIndex;
      // Move the list item
      ListBox1.Items.Move(ListBox1.ItemIndex, LNewListBoxItemIndex);
      // Preserve the selection (if applicable)
      ListBox1.ItemIndex := LNewListBoxItemIndex;
    end;
    
    您还可以决定将列表与图层完全同步。在这种情况下,您应该将每个项目(可能是
    TPanel
    )与一个图层相关联

    // Create layers from front to back
    LLayer := TBitmapLayer.Create(ImgView321.Layers);
    ListBox1.Items.AddObject('First layer', LLayer);    
    // Could use LPanel := TPanel.Create(...); LPanel.Tag := Integer(Pointer(LLayer)) instead
    
    LLayer := TBitmapLayer.Create(ImgView321.Layers);
    ListBox1.Items.AddObject('Second layer', LLayer);   
    
    // Now the list is correct but the layers are not in the right order.
    // Use the code listed below whenever you need to synchronize the layers
    // with the list. In theory it may be slow (O(n^2)) but practically it
    // won't matter much assuming you won't have hundreds of layers.
    
    // Don't update the screen every time we move a layer to get closer to the final result
    ImgView321.BeginUpdate;
    try
      for LIndex := 0 to ListBox1.Items.Count - 1 do
        // Get the associated layer and make it the least visible of all processed so far
        TCustomLayer(ListBox1.Items.Objects[LIndex]).SendToBack;
        // Could use TCustomLayer(Pointer(SomePanel.Tag)).SendToBack instead
    finally
      // Always do this not to have strange behavior after an error
      ImgView321.EndUpdate;
    end;
    // When it's done, update the screen
    ImgView321.Changed;
    

    我猜其中一个图层没有位图。但是就像我说的,如果我不改变图层索引的顺序,保存就可以了。当我把图层发送到后面的那一刻。。。出现错误。所以层是相同的,只是当崩溃发生时它们的顺序会改变哦,不要相信我,我可能错了,但是相信调试器。只需逐步完成保存过程并保存bmpaspng,然后自己检查。作为对编辑的响应。可以通过设置图层的
    索引
    属性来更改图层顺序。哇,如果这么简单。。。这样想:我有一个面板显示
    
    // Create layers from front to back
    LLayer := TBitmapLayer.Create(ImgView321.Layers);
    ListBox1.Items.AddObject('First layer', LLayer);    
    // Could use LPanel := TPanel.Create(...); LPanel.Tag := Integer(Pointer(LLayer)) instead
    
    LLayer := TBitmapLayer.Create(ImgView321.Layers);
    ListBox1.Items.AddObject('Second layer', LLayer);   
    
    // Now the list is correct but the layers are not in the right order.
    // Use the code listed below whenever you need to synchronize the layers
    // with the list. In theory it may be slow (O(n^2)) but practically it
    // won't matter much assuming you won't have hundreds of layers.
    
    // Don't update the screen every time we move a layer to get closer to the final result
    ImgView321.BeginUpdate;
    try
      for LIndex := 0 to ListBox1.Items.Count - 1 do
        // Get the associated layer and make it the least visible of all processed so far
        TCustomLayer(ListBox1.Items.Objects[LIndex]).SendToBack;
        // Could use TCustomLayer(Pointer(SomePanel.Tag)).SendToBack instead
    finally
      // Always do this not to have strange behavior after an error
      ImgView321.EndUpdate;
    end;
    // When it's done, update the screen
    ImgView321.Changed;