Delphi graphics32 сохраняет слои как прозрачный PNG

У меня странная проблема, и я не думаю, что смогу ее решить. У меня есть ImgView, содержащий слои (прозрачные изображения PNG), и я намерен сохранить все слои в виде файлов PNG (например, "Сохранить проект"), чтобы позже я мог открыть их заново и поместить туда, где я их оставил. (как в случае с "Open Project") Это моя проблема, следующие шаги работают нормально:

  1. Я добавляю слои (прозрачные файлы PNG)
  2. Я перемещаю их и помещаю их туда, где я хочу их
  3. Я нажимаю сохранить проект (поэтому здесь я сохраняю все слои в виде файлов изображений PNG)
  4. Оно работает

Если я сделаю следующие шаги, что-то пойдет не так:

  1. Я добавляю слои (прозрачные файлы PNG)
  2. Я перемещаю их и помещаю их туда, где я хочу их
  3. Я изменяю расположение слоев (как в: отправить обратно на один слой, например) (так что этот шаг отличается)
  4. Я нажимаю сохранить проект (поэтому здесь я сохраняю все слои в виде файлов изображений PNG)
  5. Сбой: "Нарушение прав доступа по адресу 005380FB в модуле" MyApp.exe ". Чтение адреса 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, пожалуйста, дайте мне знать.

РЕДАКТИРОВАТЬ

Итак, пожалуйста, обратите внимание на изображение ниже, что графический интерфейс сделан и работает. У меня есть панели, представляющие слои, я могу перемещать их (как вы можете видеть на изображении, я перетаскиваю слой "Элементул 0" и перемещаю его вверх по цепочке). И я повторяю, моя логика также работает, когда я использую временные файлы для перемещения слоев вверх или вниз в порядке. В одном из ответов предлагалось, чтобы я просто использовал свойство Index, чтобы изменить положение слоев в иерархии слоев, и я говорю, что это невозможно сделать, по крайней мере, добавив новые слои в изображение. Так что это не двойной вопрос. Это просто ответ на один из полученных мной ответов.

Спасибо

3 ответа

Ваша проблема намного проще, чем вы думаете. Работа со слоями естественна:

Отправить обратно

Установите индекс слоя на 0 или просто вызовите SendToBack, У всех слоев, ранее имевшихся до этого, их индекс будет увеличен на 1. Все слои, ранее имевшиеся после этого, останутся в том же положении.

Отправить назад

Уменьшите индекс слоя на 1. Слой ранее, прежде чем он последует за ним, таким образом, его индекс увеличился на единицу.

Отправить вперед

Увеличьте индекс слоя на 1. Слой ранее после того, как он теперь будет стоять перед ним, таким образом, его индекс уменьшился на единицу.

Отправить на фронт

Установите индекс слоя равным количеству слоев минус 1. Слой, у которого после его увеличения их значение уменьшилось на один.

Следовательно, абсолютно не нужно прикасаться к растровому изображению, сохранять его на диск или использовать любые временные слои для изменения порядка. Практически в каждом случае правильная вещь происходит, когда вы просто устанавливаете индекс слоя в положение (считая от 0, вперед), в котором вы хотите, чтобы он отображался. Переместив панель в свой список, вы можете установить индекс соответствующего слоя на новый индекс панели в списке. Однако, поскольку панель упорядочена спереди назад, а GR32 упорядочена спереди назад, вам нужно перевести индекс панели в нужный индекс слоя.

Вот пример, как это сделать с TListBox и 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;

По вашему описанию того, как вы изменили порядок слоев, это, скорее всего, причина вашей проблемы. Поскольку вы не опубликовали эту часть кода, она не может быть оценена с уверенностью.

В любом случае, чтобы переставить слои, вы можете использовать Index собственностью TCustomLayer (из которых TBitmapLayer потомок)

Таким образом, решение проблемы состоит в том, чтобы НЕ использовать Bitmap32List в качестве временного контейнера для слоев png при переупорядочении слоев, потому что что-то теряется в процессе. Так что попробуйте другое решение для переупорядочения. Мое настоящее решение - сбросить слои в виде файлов PNG на диск, а затем перезагрузить их с диска в нужном порядке. Другое решение (еще не проверенное) - создать несколько новых слоев, равное количеству существующих слоев, переместить туда реальные слои, затем вернуть их один за другим в нужном порядке, а затем удалить дополнительные слои.

Тем не мение. Это был вопрос, и это ответ до сих пор

Другие вопросы по тегам