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

时间:2015-01-25 05:31:21

标签: delphi graphics32

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

  1. 我添加图层(透明PNG文件)
  2. 我将它们移动并放置在我想要的地方
  3. 我按保存项目(所以这里我将所有图层保存为png图像文件)
  4. 工作
  5. 如果我执行以下后续步骤,则会出现问题:

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

      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'并在链中移动它)。 enter image description here 我再说一遍,当我使用临时文件在订单中向上或向下移动图层时,我的逻辑也有效。其中一个答案表明我应该使用Index属性来更改图层层次结构中的图层位置,我说如果不至少在图像中添加新图层就无法完成。所以这不是一个双重问题。这只是对我收到的答案之一的回应。

      谢谢

3 个答案:

答案 0 :(得分:2)

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

发送回

将图层索引设置为0或只需调用SendToBack之前的所有图层的索引都会增加1.之前的所有图层都会保持在同一位置。

向后发送

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

发送

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

发送到前面

将图层的索引设置为图层数减1. 之前的图层增加的图层减少了一个。

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

以下是如何使用TListBoxTButton执行此操作的示例:

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;

答案 1 :(得分:1)

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

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

答案 2 :(得分:0)

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

反正。这就是问题,到目前为止这是答案