Работа с растровыми изображениями. Вопросы

Автор Тема: Работа с растровыми изображениями. Вопросы  (Прочитано 7041 раз)

0 Пользователей и 2 Гостей просматривают эту тему.

Оффлайн NomadVАвтор темы

  • ADN OPEN
  • Сообщений: 7
  • Карма: 0
С помощью статей на этом сайте и на AutocadDevBlog написал программу, которая проверяет наличие в чертеже вхождений растровых изображений и выполняет с ними определенные действия  (меняет пути, удаляет выгруженные и неиспользуемые вхождения и т.д.). Все вроде работает, но есть ряд вопросов. Ниже листинг:

Код - C# [Выбрать]
  1. Document doc = Application.DocumentManager.MdiActiveDocument;
  2.             Database db = doc.Database;
  3.             Editor ed = doc.Editor;
  4.  
  5.             ObjectId imageDict = RasterImageDef.GetImageDictionary(db);
  6.  
  7.             if (imageDict == ObjectId.Null)
  8.             {
  9.                 ed.WriteMessage("No images in the drawing.\n");
  10.                 return;
  11.             }
  12.  
  13.             using (Transaction Tx = db.TransactionManager.StartTransaction())
  14.             {
  15.                 DBDictionary ImageDic = (DBDictionary)Tx.GetObject(imageDict, OpenMode.ForWrite);
  16.  
  17.                 foreach (DBDictionaryEntry ImageDef in ImageDic)
  18.                 {
  19.                     //Получаем определение растрового изображения
  20.                     RasterImageDef imageDef = (RasterImageDef)Tx.GetObject(ImageDef.Value, OpenMode.ForWrite);
  21.                    
  22.                     if (imageDef.IsLoaded)
  23.                     {
  24.                         //изображение может иметь статус "загружено" или "не используется"
  25.                         int ImageCount = 0;
  26.                         ObjectIdCollection ids = imageDef.GetPersistentReactorIds();
  27.                         foreach (ObjectId id in ids)
  28.                         {
  29.                             DBObject reactor = Tx.GetObject(id, OpenMode.ForRead);
  30.                             string name = reactor.GetRXClass().DxfName;
  31.                             if (string.Compare(name,"IMAGEDEF_REACTOR", true) == 0)
  32.                             {
  33.                                 if (!reactor.OwnerId.IsErased)
  34.                                 {
  35.                                     DBObject obj = Tx.GetObject(reactor.OwnerId, OpenMode.ForRead);
  36.                                     if (obj is RasterImage) ImageCount++;
  37.                                 }
  38.                             }
  39.                         }
  40.                         if (ImageCount > 0)
  41.                         {
  42.                             ed.WriteMessage(imageDef.ActiveFileName + " : загружено и имеет " + ImageCount + " вхождений.\n");
  43.                             //Проверить пути и при необходимости изменить
  44.                         }
  45.                         else
  46.                         {
  47.                             ed.WriteMessage(imageDef.ActiveFileName + " : не используется.\n");
  48.                             //удалить
  49.                             //RasterImage.EnableReactors(false);
  50.                             //imageDef.Unload(true);
  51.                             imageDef.Erase();
  52.                         }
  53.  
  54.  
  55.                        
  56.                     }
  57.                     else
  58.                     {
  59.                         //изображение может иметь статус "не найдено" или "выгружено"
  60.                         if (imageDef.ActiveFileName.Length == 0)
  61.                         {
  62.                             ed.WriteMessage(imageDef.SourceFileName + " : не найдено" + "\n");
  63.                             //Здесь будет поиск файла изображения в определенном каталоге и,
  64.                             //в случае удачи, изменение imageDef.SourceFileName
  65.                         }
  66.                         else
  67.                         {
  68.                             ed.WriteMessage(imageDef.ActiveFileName + " : выгружено" + "\n");
  69.                             //удалить
  70.                             //RasterImage.EnableReactors(false);
  71.                             //imageDef.Unload(true);
  72.                             imageDef.Erase();
  73.                         }
  74.                     }
  75.                 }
  76.                 Tx.Commit();
  77.             }
  78.             db.ResolveXrefs(false, false);

А теперь вопросы:
1) Во фрагменте
Код - C# [Выбрать]
  1.  if (!reactor.OwnerId.IsErased)
  2.                                 {
  3.                                     DBObject obj = Tx.GetObject(reactor.OwnerId, OpenMode.ForRead);
  4.                                     if (obj is RasterImage) ImageCount++;
  5.                                 }
не очень понятен смысл условия.
2) Для чего отключаются реакторы перед удалением определения растрового изображения?
Код - C# [Выбрать]
  1. RasterImage.EnableReactors(false);
Я закомментировал эту строку - без отключения тоже работает. Чем чревато, если не отключать? Надо ли потом включать? Или может в начале программы отключить, а в конце включить?
3)Когда перед удалением вхождения растра надо его сначала выгружать?
Код - C# [Выбрать]
  1. imageDef.Unload(true);
« Последнее редактирование: 05-06-2014, 16:04:31 от Александр Ривилис »

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
Добрый день. Приветствую на нашем форуме:
1.
Код - C# [Выбрать]
  1.  if (!reactor.OwnerId.IsErased)
Идет проверка не удален ли владелец реактора.
Код - C# [Выбрать]
  1. if (obj is RasterImage) ImageCount++;
Если владелец реактора имеет тип  RasterImage - увеличиваем счетчик.
Т.е. нас интересует не удаленные RasterImage - только их количество мы и подсчитываем.
2.
Код - C# [Выбрать]
  1. RasterImage.EnableReactors(false);
Реакторы реагируют на модификацию объекта-владельца. Соответственно если не отключить реакторы, то несмотря на то, что владелец уже удален, реакторы сработают. И вот тут может возникнуть проблема, которая может привести к краху AutoCAD. Так что я советую эту строку оставить как есть.
3.
Код - C# [Выбрать]
  1. imageDef.Unload(true);
По этому поводу читаем документацию, которая говорит, что этот метод немедленно освобождает (оперативную) память занятую растровым изображением. Так что вызывать его или нет - это дело вкуса. Если растр достаточно большой (а они бывают и десятки, и сотни мегабайт), то думаю лучше вызвать этот метод. В любом случае если чертеж будет сохранен, когда imageDef удален, то при следующем открытии этого чертежа он уже не будет знать про imageDef и соответственно не будет пытаться загрузить растр и оперативная память не будет занята им.

Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн NomadVАвтор темы

  • ADN OPEN
  • Сообщений: 7
  • Карма: 0
Реакторы реагируют на модификацию объекта-владельца. Соответственно если не отключить реакторы, то несмотря на то, что владелец уже удален, реакторы сработают. И вот тут может возникнуть проблема, которая может привести к краху AutoCAD. Так что я советую эту строку оставить как есть.
А включать реакторы после всех манипуляций есть смысл? Может быть вообще в начале процедуры их отключить, а в конце включить (если таки надо включать)?

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
А включать реакторы после всех манипуляций есть смысл?
В принципе да. В противном случае для тех ImageDef, которые не удалены и для которых в дальнейшем будут производится изменения в этом сеансе работы с AutoCAD, не будет реакции (изменения внешнего вида) в соответствующих RasterImage.
Я более глубоко полез разбираться с тем, что делает RasterImage.EnableReactors() и выяснил, что он производит вызов неуправляемого кода AcDbRasterImageDefReactor::setEnable
В документации к нему написано, что его следует использовать для временного отключения реакторов при модификации и/или удалении ImageDef.

Может быть вообще в начале процедуры их отключить, а в конце включить
Думаю что это будет правильно.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн NomadVАвтор темы

  • ADN OPEN
  • Сообщений: 7
  • Карма: 0
А если программа будет работать не с активным документом, а например загружать базу
Код - C# [Выбрать]
  1. dwgDB.ReadDwgFile(dwgFilePath, System.IO.FileShare.ReadWrite, false, "");
вообще без открытия чертежа в окне Autocad, это что-то меняет в плане обращения с реакторами и выгрузки ссылок перед удалением?
Кстати, в ходе тестирования программы столкнулся с одним неучтенным моментом. По задумке определения растровых изображений со статусом "не используется" в окне внешних ссылок у меня подлежат удалению. На всякий случай: такой статус получается, когда пользователь удаляет растровое изображение не через окно внешних ссылок, а просто выделив и удалив все вхождения. Проверка imageDef.IsLoaded дает истину, если файл растрового изображения доступен, и ложь, если файл недоступен. Так что для однозначного определения не используемого растра, надо в любом случае считать количество вхождений. Если кому пригодится, привожу исправленный листинг:
Код - C# [Выбрать]
  1. Document doc = Application.DocumentManager.MdiActiveDocument;
  2.             Database db = doc.Database;
  3.             Editor ed = doc.Editor;
  4.  
  5.             ObjectId imageDict = RasterImageDef.GetImageDictionary(db);
  6.  
  7.             if (imageDict == ObjectId.Null)
  8.             {
  9.                 ed.WriteMessage("No images in the drawing.\n");
  10.                 return;
  11.             }
  12.  
  13.             RasterImage.EnableReactors(false);//Отключаем реакторы во избежание возможных проблем при изменении и удалении растровых вхождений
  14.  
  15.             using (Transaction Tx = db.TransactionManager.StartTransaction())
  16.             {
  17.                 DBDictionary ImageDic = (DBDictionary)Tx.GetObject(imageDict, OpenMode.ForWrite);
  18.  
  19.                 foreach (DBDictionaryEntry ImageDef in ImageDic)
  20.                 {
  21.                     //Получаем определение растрового изображения
  22.                     RasterImageDef imageDef = (RasterImageDef)Tx.GetObject(ImageDef.Value, OpenMode.ForWrite);
  23.                     //Посчитаем количество вхождений изображения
  24.                     int ImageCount = 0;
  25.                     ObjectIdCollection ids = imageDef.GetPersistentReactorIds();
  26.                     foreach (ObjectId id in ids)
  27.                     {
  28.                         DBObject reactor = Tx.GetObject(id, OpenMode.ForRead);
  29.                         string name = reactor.GetRXClass().DxfName;
  30.                         if (string.Compare(name, "IMAGEDEF_REACTOR", true) == 0)
  31.                         {
  32.                             if (!reactor.OwnerId.IsErased)
  33.                             {
  34.                                 DBObject obj = Tx.GetObject(reactor.OwnerId, OpenMode.ForRead);
  35.                                 if (obj is RasterImage) ImageCount++;
  36.                             }
  37.                         }
  38.                     }
  39.                     //Растровое изображение имеет статус "Не используется"
  40.                     if (ImageCount == 0)
  41.                     {
  42.                         ed.WriteMessage(imageDef.SourceFileName + " : не используется.\n");
  43.                         //удалить
  44.                         imageDef.Unload(true);
  45.                         imageDef.Erase();
  46.                         continue;
  47.                     }
  48.  
  49.                     //Растровое изображение имеет статус "Загружено"
  50.                     if (imageDef.IsLoaded)
  51.                     {
  52.                         ed.WriteMessage(imageDef.ActiveFileName + " : загружено и имеет " + ImageCount + " вхождений.\n");
  53.                         //Проверить пути и при необходимости изменить
  54.                        
  55.                     }
  56.                     else
  57.                     {
  58.                         //изображение может иметь статус "не найдено" или "выгружено"
  59.                         if (imageDef.ActiveFileName.Length == 0)
  60.                         {
  61.                             ed.WriteMessage(imageDef.SourceFileName + " : не найдено" + "\n");
  62.                             //Здесь будет поиск файла изображения в определенном каталоге и,
  63.                             //в случае удачи, изменение imageDef.SourceFileName
  64.                         }
  65.                         else
  66.                         {
  67.                             ed.WriteMessage(imageDef.ActiveFileName + " : выгружено" + "\n");
  68.                             imageDef.Erase();
  69.                         }
  70.                     }
  71.                 }
  72.                 Tx.Commit();
  73.             }
  74.  
  75.             RasterImage.EnableReactors(true);//Включаем реакторы
  76.             db.ResolveXrefs(false, false);


Пользуйся тегами [code=csharp]...код...[/code] - /* Александр Ривилис */
« Последнее редактирование: 06-06-2014, 18:48:37 от Александр Ривилис »

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
А если программа будет работать не с активным документом, а например загружать базу
Код - C# [Выбрать]
  1. dwgDB.ReadDwgFile(dwgFilePath, System.IO.FileShare.ReadWrite, false, "");

вообще без открытия чертежа в окне Autocad, это что-то меняет в плане обращения с реакторами и выгрузки ссылок перед удалением?
В принципе код должен быть точно таким же. Но на всякий случай советую проверить - иногда AutoCAD преподносит нам неожиданные (и не всегда приятные) сюрпризы. 
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение