-1

我有一个奇怪的问题,我不认为我可以解决它。我有一个包含图层(透明 png 图像)的 ImgView,我打算将所有图层保存为 png 文件(如“保存项目”的东西),以便稍后我可以重新打开它们并将它们放在我离开它们的地方。(就像“打开项目”一样)这是我的问题,以下步骤可以正常工作:

  1. 我添加图层(透明的 PNG 文件)
  2. 我移动它们并将它们放在我想要它们的地方
  3. 我按保存项目(所以在这里我将所有图层保存为 png 图像文件)
  4. 有用

如果我执行以下步骤,则会出现问题:

  1. 我添加图层(透明的 PNG 文件)
  2. 我移动它们并将它们放在我想要它们的地方
  3. 我更改了图层的位置(例如:发送回一层)(所以这一步是不同的)
  4. 我按保存项目(所以在这里我将所有图层保存为 png 图像文件)
  5. 它因“模块'MyApp.exe'中地址005380FB的访问冲突。读取地址000000C0”而崩溃

现在它只给了我上面的错误,但在几次运行前,它把我指向了这一行:

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

因此,如果我更改图层的索引...我不能再将它们另存为 PNG ?!?!?!

这是我的保存过程:

  for i:=0 to mainForm.ImgView.Layers.Count-2 do
  begin
    mylay := TBitmapLayer(mainForm.ImgView.Layers.Items[i]);
    SaveBMPAsPng(mylay.Bitmap,'C:\MyApp\tmp\'+getLayerPan(i)+'.png');
  end;
// where getLayerPan is a function that retrieves a name that I gave to the layer

... 和

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;

有什么问题?请帮忙...

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

编辑

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

谢谢

4

3 回答 3

2

你的问题比你想象的要简单得多。使用图层很自然:

发回

将图层的索引设置为 0 或简单地调用SendToBack. 在它之前的所有层的索引都会增加1。之前的所有层都保持在相同的位置。

向后发送

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

转发

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

发送到前面

将层的索引设置为层数减1。之前增加的层减一。

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

这是一个如何使用 aTListBox和 a执行此操作的示例TButton

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;
于 2015-01-25T15:10:55.613 回答
1

根据您对如何更改图层顺序的描述,这很可能是您的问题的原因。由于您没有发布那部分代码,因此无法确定地评估。

无论如何,要重新排列图层,您可以使用(of which is a descendant)的Index属性TCustomLayerTBitmapLayer

于 2015-01-25T10:44:11.790 回答
0

所以这个问题的解决方案是在重新排序图层时不要使用 Bitmap32List 作为 png 图层的临时容器,因为在这个过程中会丢失一些东西。因此,请尝试其他解决方案进行重新排序。我目前的解决方案是将图层作为 PNG 文件放到磁盘上,然后以所需的顺序从磁盘重新加载它们。另一种解决方案(尚未测试)是创建多个新层,等于现有层的数量,将实际层移到那里,然后以所需的顺序将它们一个接一个地取回,然后删除额外的层。

反正。这就是问题,这是迄今为止的答案

于 2015-01-25T10:46:09.660 回答