Здравствуйте, коллеги-программисты. Я новичок на форуме, поэтому, пожалуйста, будьте терпеливы.
Чего я пытаюсь достичь, так это взять загруженное изображение из файла (например, фоновое изображение обоев 4000x2250) и уменьшить его до собственного разрешения 1360x768. Точно так же, как в современной ОС вы устанавливаете изображение обоев для рабочего стола.
typedef struct {
UINT8 Blue;
UINT8 Green;
UINT8 Red;
UINT8 Reserved;
} EFI_GRAPHICS_OUTPUT_BLT_PIXEL;
typedef EFI_GRAPHICS_OUTPUT_BLT_PIXEL GUI_PIXEL;
typedef struct {
UINT32 Width;
UINT32 Height;
BOOLEAN HasAlpha;
GUI_PIXEL *PixelData;
} GUI_IMAGE;
GUI_IMAGE* ReduceImage(GUI_IMAGE* image)
{
UINT32 resizeWidth = image->Width / 2;
UINT32 resizeHeight = image->Height / 2;
UINT32 x;
UINT32 y;
UINT32 row = 0;
UINT32 column = 0;
GUI_IMAGE *NewImage;
NewImage = AllocateZeroPool(sizeof(GUI_IMAGE));
if (NewImage == NULL)
{
return NULL;
}
NewImage->PixelData = AllocateZeroPool(resizeWidth * resizeHeight *
sizeof(GUI_PIXEL));
if (NewImage->PixelData == NULL)
{
FreePool(NewImage);
return NULL;
}
NewImage->Width = resizeWidth;
NewImage->Height = resizeHeight;
NewImage->HasAlpha = TRUE;
for(y = 0; y < resizeHeight - 1; y++)
{
for(x = 0; x < resizeWidth - 1; x++)
{
NewImage->PixelData[(resizeWidth) * y + x] = image->PixelData[(image->Width) * row + column];
column += 2;
}
row += 2;
column = 0;
}
return NewImage;
}
void ReduceImage( ..., GUI_IMAGE* result,...
Большой красный флаг... Вызывающий объект не имеет доступа к буферу, выделенному в этой функции... Вероятно, нужен двойной указатель, но представленного кода недостаточно, чтобы точно знать, что происходит...
@ chux - Восстановить Монику - код написан так, чтобы запускаться только в том случае, если ширина/высота исходного изображения больше целевой высоты ширины...
@ Fe2O3 — результат GUI_IMAGE* на самом деле является глобальным в моем файле C, если это помогает.
То, как вы вызываете ReduceImage()
, действительно важно (и не показано?) из-за параметров targetWidth
и targetHeight
. . . они действительно должны быть меньше ширины и высоты исходного изображения соответственно. Они также должны быть в пределах. Я знаю, что это элементарно, и велика вероятность, что вам это небезразлично, но… . . это будет моя первая проверка в духе перехода от простого к сложному методу отладки.
@ Грег Спирс — Мой вызов — «ReduceImage(LoadedImageFromFile(например, *.jpeg, *.png, *.bmp), GlobalImage, NativeWidth, NativeHeight); Все загруженные изображения намеренно больше исходного разрешения экрана. Я изменил часть кода, что устранило зависание, но конечным результатом является шестнадцатеричное значение без заголовка и конца, что делает изображение неотображаемым.
@Mike «Результат GUI_IMAGE* на самом деле является глобальным». Это не имеет значения. Переменная параметра с тем же именем затеняет глобальную переменную. (Распечатайте оба адреса (%p), чтобы увидеть разницу.) Локальная переменная (параметр) используется внутри тела функции, НЕ оказывая НИКАКОГО влияния на значение «глобальной» переменной... Быстрое решение: удалите этот параметр из функции сбор и использовать только «глобальную» переменную... Нехорошо, но на данный момент это исправит этот код...
@Fe2O3 — Внес изменения в код согласно вашему предложению, однако результат тот же… Изображение представляет собой искаженную визуализацию ширины экрана на 0,5 дюйма площади пикселя.
@MikeAmbrose Никто не сможет помочь вам с кодом, который они не видят. (Imo) явная проблема, на которую я указал с этой единственной функцией, предполагает, что весь код может быть пронизан ошибками. Вы не отнесете запаску механику и не спросите, почему не запускается двигатель. Это ничем не отличается.
@MikeAmbrose Прочтите и подумайте об этих недавних вопросах и ответах. Позиция: ваше исходное изображение имеет размер 1000x1000, а вы хотите, чтобы оно было 100x100. Как написано, этот код выполнит 10^6 циклов, чтобы получить 10^4 пикселей. Не хорошо. Представьте себе, что циклы заполняют каждый целевой пиксель, вычисляя, какой исходный пиксель используется... Хотя это будет быстро, исключение 99% циклов (в этом примере) было бы целесообразным, не так ли?
@ Fe2O3 Fe2O3 — я переписал всю функцию с нуля, и она отлично работает, уменьшая изображение вдвое. Мой следующий вопрос: как я могу оптимизировать код для скорости и как я могу написать его, чтобы уменьшить изображение до разрешения экрана?
@MikeAmbrose Код выглядит лучше (хотя и не видит всего). Как масштабировать? Положите две линейки рядом, одна — «см», а другая — «дюймы»… Изучите, где совпадают значения «целого числа», и подумайте, как вы масштабируете, чтобы получить 12 дюймов из 30 см. Обе оси x и y прямоугольника src и dst будут иметь свой собственный «коэффициент масштабирования». Это базовая математика со школьных времен. Чтобы получить dst-пиксель для 1 дюйма, используйте src-пиксель на расстоянии 2,56 см. Поскольку есть только пиксели 2 и 3, вам нужно округлить и взять src-пиксель №3. Промыть, прополоскать, повторить... Оптимизация не является задачей беспокойство, пока все не заработает.
Спасибо за всю помощь! Я понял.
#define RoundValue(x) ((x)>=0?(long)((x)+0.5):(long)((x)-0.5))
GUI_IMAGE* ScaleImage(GUI_IMAGE* image)
{
float ratio; // >1 is enlarging(e.g. 1.1), <1 is reducing(e.g. 0.4)
UINT32 resWidth;
UINT32 resHeight;
UINT32 x;
UINT32 y;
UINT32 x_in;
UINT32 y_in;
GUI_IMAGE *NewImage;
GetResolution(&resWidth, &resHeight);
ratio = (float)resWidth / (float)image->Width;
NewImage = AllocateZeroPool(sizeof(GUI_IMAGE));
if (NewImage == NULL)
{
return NULL;
}
NewImage->PixelData = AllocateZeroPool(resWidth * resHeight * sizeof(GUI_PIXEL));
if (NewImage->PixelData == NULL)
{
FreePool(NewImage);
return NULL;
}
NewImage->Width = resWidth;
NewImage->Height = resHeight;
NewImage->HasAlpha = TRUE;
for(y = 0; y < resHeight; y++)
{
for(x = 0; x < resWidth; x++)
{
x_in = RoundValue((float)x / ratio);
y_in = RoundValue((float)y / ratio);
NewImage->PixelData[resWidth * y + x] = image->PixelData[image->Width * y_in + x_in];
}
}
return NewImage;
}
Поздравляю... Однако есть вот что, с которым код работает не совсем правильно. Вы можете решить, хотите ли вы иметь черные рамки слева/справа или сверху/снизу, ИЛИ изображение должно быть растянуто/сжато, чтобы при переключении между двумя разными «соотношениями сторон» все равно заполнялась область отображения места назначения. (Или даже «обрезать» один или два края, которые могут быть слишком широкими/высокими.) Удачи!
@Fe2O3 Fe2O3 - Можете ли вы показать мне, как можно реализовать, скажем, соотношение сторон 16:9 в приведенном выше коде?
Если исходное и целевое соотношение сторон 16:9, проблем нет. Аналоговые фотографии бывают разных размеров; 3x5, 4x6, 8x10... У каждого разное соотношение сторон. Поиграйтесь с любым программным обеспечением для редактирования изображений (даже с Windows Paint)... Я не могу научить вас всему в обмене комментариями здесь. Начните читать, делать заметки, исследовать и думать. Найдите здесь, на SO, «фильтр Собеля». Это не масштабирование, а манипуляция изображением. Удачи!
@ Fe2O3 - Спасибо за направление, отзывы, идеи и т. д. Однако немного кода для анализа или использования было бы здорово. Как указано в теме, я работаю с относительно большими изображениями, такими как обои для рабочего стола компьютера, а не с изображениями и фотографиями стандартного размера. На данный момент код не поврежден и работает, и его можно легко расширить по желанию.
Код не так уж и плох, но... Работа с плавающей запятой медленнее, чем целочисленные вычисления, И это рискует UB... 12 * 2,56 = 30,72... Если код ожидает найти «максимальный» соответствующий пиксель в 31
код будет доступен за пределами массива... Предлагаю опубликовать новый вопрос здесь или на сайте «Просмотр кода» для получения дополнительной информации. Молодец, что ты переписал и получил что-то гораздо лучшее, чем изначально! Продолжать идти!!
«на экране отображается обрезанное изображение» -> происходит ли это, когда изображение целевого размера совпадает с оригиналом?