Нужно отмасштабировать битмапку в памяти с заданными зумом. Вчера половину дня промаялся: пищит, мигает, известно куда не попадает.
Все масштабирует, зум на лицо, но обрезает по размеру исходого битмапа. Полный: *опа-кеды.
Уважаемый All, ткните носом, ЧЯДНТ!
HBITMAP CDlgPicResize::DoResizePlus(const LONG cx /*новая ширина для битмпап*/, const LONG cy /*новая высота*/)
{
Gdiplus::Status st;
TRACE(TEXT("DoResizePlus cx=%d cy=%d\n"),cx,cy);
//расcчитываем Zoom-фактор const float cx_factor=((float)cx/(float)m_szSRC.cx);//(m_szSRC - CSize с исходными размерами 1 в 1)const float cy_factor=((float)cy/(float)m_szSRC.cy);
TRACE(TEXT("cx_factor=%f, CY_factor=%f\n"),cx_factor,cy_factor);
const HDC hdcFrom=::GetDC(m_hwndRE);//HWND куда потом будет вставлен новый битпа
ASSERT(hdcFrom);
//////////////////////////////////////////////////////////////////////////
//все эти с CAutoGDI*** - это auto-объекты, которые только освобождают ресурсы GDI на выходеconst CAutoGDIHDC_Release release_dc_wnd(hdcFrom,m_hwndRE);
//создаем битмап на котором будем рисовать в памяти
HBITMAP hNew=CreateCompatibleBitmap(hdcFrom,cx,cy);
ASSERT(hNew);
//создаем временный GDI+ битмап и выбираем его в контекст
Gdiplus::Bitmap bmpForDraw(cx
,cy
,PixelFormat24bppRGB) ;
Gdiplus::Graphics g(&bmpForDraw);
g.SetClip(Gdiplus::Rect(0,0,cx,cy)) ;
st = g.SetInterpolationMode(Gdiplus::InterpolationModeBicubic);
g.SetCompositingQuality(Gdiplus::CompositingQualityHighQuality);
//устанавливаем масштабирование
g.ScaleTransform(cx_factor, cy_factor);
{/// рисуем исходный битмап с маштабированием и новой шириной\высотой
Gdiplus::Bitmap bmpTemp(m_hSource,NULL);// m_hSource это HBITMAP исходный
g.DrawImage(&bmpTemp,0,0,cx,cy);
//забираем битмап
HBITMAP h=NULL;
st = bmpForDraw.GetHBITMAP(RGB(255,0,0),&h);
ASSERT(Gdiplus::Ok == st);
ASSERT(h);
ASSERT(h != hNew);
BITMAP bNew={0};
GetObject(h, sizeof(BITMAP),&bNew);
TRACE(TEXT("IN: cx=%d cy=%d\n"),bNew.bmWidth,bNew.bmHeight);
hNew=h;//ДЛЯ кодо-копателей: да здесь идет потеря вышесозданного HBITMAP, но это издержки тестирования
//, поскольку уже 100 раз переписывалось и так и эдак,
//изначально все корректно было, и потом, когда доводить буду - “причешу”.
}
#ifdef _DEBUG
//всяко разно дебажные проверки (ширина\высота нового битмапа соответсвуют?
//для просмотра кидаем в файл
BITMAP bNew={0};
GetObject(hNew , sizeof(BITMAP),&bNew);
TRACE(TEXT("new cx=%d cy=%d\n"),bNew.bmWidth,bNew.bmHeight);
{
Gdiplus::Bitmap bmpForSave(hNew,NULL);
CLSID bmpClsid ={0};
GetCodecClsid(L"image/bmp", &bmpClsid);
st= bmpForSave.Save(L"C:\\Test_GDI.bmp",&bmpClsid,NULL);
ASSERT(Gdiplus::Ok == st);
}
#endif
return hNew;
}
Проблема в следующем: все масштабируется, но на выходе получаем обрезанный битмап по размеру исходного. То бишь зум видно невооруженным взглядом — все чики-поки. Но если масштаб больше 100 — тогда обрезается по размеру исходного битмапа. Если масштаб меньше 100 — то опять же, все масштабируется, результирующий битмап ессесна получается меньше исходого. Но опять же все по размеру исходного, просто с черным фоном.
Ни черта не понимаю уже. Что я не так то хоть делаю!?! Уже только что не перепробовал, не попеременял в коде. Ан фиг! Хелп, коллеги!
Попробуй через g.SetTransform которая матрицу преобразований в себя принимает, на MSDN должно быть описание этих преобразований — масштаб, сдвиг и вращение.
Тут еще статьи можешь посмотреть, там есть про двойную буферизацию чтоб не мерцало http://rsdn.org/summary/625.xml
Здравствуйте, Serpuh, Вы писали:
S>Попробуй через g.SetTransform которая матрицу преобразований в себя принимает, на MSDN должно быть описание этих преобразований — масштаб, сдвиг и вращение. S>Тут еще статьи можешь посмотреть, там есть про двойную буферизацию чтоб не мерцало http://rsdn.org/summary/625.xml
Спасибо! Полистал… Но не то всё.
Двойная буферизация мне не нужна, ибо у меня и так все в памяти рисуется. Проблема в том, что отсекается все вне исходного битмапа после масштабирования.
Методика тщательной медитации дала следующее: Visible Rect сбивается у Graphics как только я выставляю ScaleTransform
HBITMAP hNew=CreateCompatibleBitmap(hdcFrom,cx,cy);//создали битмап в памяти с новой шириной высотой
ASSERT(hNew);
Gdiplus::Bitmap bmpForDraw(cx
,cy
,PixelFormat24bppRGB) ;
Gdiplus::Graphics* gp=Gdiplus::Graphics::FromImage(&bmpForDraw);
Gdiplus::Graphics& g=*gp;
//тут все ок GetVisibleClipBounds возвращает Rect с новой шириной\высотой
Gdiplus::Rect rClipVis;
g.GetVisibleClipBounds(&rClipVis);
TRACE(TEXT("visible cx=%d cy=%d\n"),rClipVis.Width,rClipVis.Height);
st = g.SetPageUnit(Gdiplus::UnitPixel);
st = g.SetInterpolationMode(Gdiplus::InterpolationModeHighQualityBicubic);
g.SetCompositingQuality(Gdiplus::CompositingQualityHighQuality);
//устанавливаем масштабирование
st = g.SetClip(Gdiplus::Rect(0,0,cx,cy)) ;
//и тут все ок GetVisibleClipBounds возвращает Rect с новой шириной\высотой
g.GetVisibleClipBounds(&rClipVis);
//ставим зум
st = g.ScaleTransform(cx_factor, cy_factor);
//!!!!!
//А ВОТ тут уже GetVisibleClipBounds возвращает Rect со старой шириной\высотой
g.GetVisibleClipBounds(&rClipVis);
//ФИга с два, повторная установка ничего не устанавливает, GetVisibleClipBounds все равно возвращает
//СТАРУЮ ширину высоту
st = g.SetClip(Gdiplus::Rect(0,0,cx,cy)) ;
g.GetVisibleClipBounds(&rClipVis);
TRACE(TEXT("visible cx=%d cy=%d\n"),rClipVis.Width,rClipVis.Height);
Чего делать не пойму. Почему ScaleTransform меняет Visible Rect непонятно… Если закомментировать ScaleTransform, то масштабирования просто не происходит, что вероятно и следовало ожидать…
...
C> //ФИга с два, повторная установка ничего не устанавливает, GetVisibleClipBounds все равно возвращает
C> //СТАРУЮ ширину высоту
C> st = g.SetClip(Gdiplus::Rect(0,0,cx,cy)) ;
C> g.GetVisibleClipBounds(&rClipVis);
C> TRACE(TEXT("visible cx=%d cy=%d\n"),rClipVis.Width,rClipVis.Height);
C>
C>Чего делать не пойму. Почему ScaleTransform меняет Visible Rect непонятно… Если закомментировать ScaleTransform, то масштабирования просто не происходит, что вероятно и следовало ожидать…
C>Чего не так… Не пойму
st = g.SetClip(Gdiplus::Rect(0,0,cx/cx_factor,cy/cy_factor)) ;
Здравствуйте, kov_serg, Вы писали:
C>>Чего делать не пойму. Почему ScaleTransform меняет Visible Rect непонятно… Если закомментировать ScaleTransform, то масштабирования просто не происходит, что вероятно и следовало ожидать…
C>>Чего не так… Не пойму
_>
_>Так не пашет?
Не-а. Все то же самое — масштабирует, это видно. Но обрезает(или дополняет черным фоном, если результирующий меньше) по исходному битмапу
C>Нужно отмасштабировать битмапку в памяти с заданными зумом. Вчера половину дня промаялся: пищит, мигает, известно куда не попадает. C>Все масштабирует, зум на лицо, но обрезает по размеру исходого битмапа. Полный: *опа-кеды. C>Уважаемый All, ткните носом, ЧЯДНТ!
C>
C>HBITMAP CDlgPicResize::DoResizePlus(const LONG cx /*новая ширина для битмпап*/, const LONG cy /*новая высота*/)
C>{
C> Gdiplus::Status st;
C> TRACE(TEXT("DoResizePlus cx=%d cy=%d\n"),cx,cy);
C> //расcчитываем Zoom-фактор
C> const float cx_factor=((float)cx/(float)m_szSRC.cx);//(m_szSRC - CSize с исходными размерами 1 в 1)
C> const float cy_factor=((float)cy/(float)m_szSRC.cy);
C> TRACE(TEXT("cx_factor=%f, CY_factor=%f\n"),cx_factor,cy_factor);
C> const HDC hdcFrom=::GetDC(m_hwndRE);//HWND куда потом будет вставлен новый битпа
C> ASSERT(hdcFrom);
C> //////////////////////////////////////////////////////////////////////////
C> //все эти с CAutoGDI*** - это auto-объекты, которые только освобождают ресурсы GDI на выходе
C> const CAutoGDIHDC_Release release_dc_wnd(hdcFrom,m_hwndRE);
C> //создаем битмап на котором будем рисовать в памяти
C> HBITMAP hNew=CreateCompatibleBitmap(hdcFrom,cx,cy);
C> ASSERT(hNew);
C> //создаем временный GDI+ битмап и выбираем его в контекст
C> Gdiplus::Bitmap bmpForDraw(cx
C> ,cy
C> ,PixelFormat24bppRGB) ;
C> Gdiplus::Graphics g(&bmpForDraw);
C> g.SetClip(Gdiplus::Rect(0,0,cx,cy)) ;
C> st = g.SetInterpolationMode(Gdiplus::InterpolationModeBicubic);
C> g.SetCompositingQuality(Gdiplus::CompositingQualityHighQuality);
C> //устанавливаем масштабирование
C> g.ScaleTransform(cx_factor, cy_factor);
C> {/// рисуем исходный битмап с маштабированием и новой шириной\высотой
C> Gdiplus::Bitmap bmpTemp(m_hSource,NULL);// m_hSource это HBITMAP исходный
C> g.DrawImage(&bmpTemp,0,0,cx,cy);
C> //забираем битмап
C> HBITMAP h=NULL;
C> st = bmpForDraw.GetHBITMAP(RGB(255,0,0),&h);
C> ASSERT(Gdiplus::Ok == st);
C> ASSERT(h);
C> ASSERT(h != hNew);
C> BITMAP bNew={0};
C> GetObject(h, sizeof(BITMAP),&bNew);
C> TRACE(TEXT("IN: cx=%d cy=%d\n"),bNew.bmWidth,bNew.bmHeight);
C> hNew=h;//ДЛЯ кодо-копателей: да здесь идет потеря вышесозданного HBITMAP, но это издержки тестирования
C>//, поскольку уже 100 раз переписывалось и так и эдак,
C>//изначально все корректно было, и потом, когда доводить буду - “причешу”.
C> }
C>#ifdef _DEBUG
C> //всяко разно дебажные проверки (ширина\высота нового битмапа соответсвуют?
C> //для просмотра кидаем в файл
C> BITMAP bNew={0};
C> GetObject(hNew , sizeof(BITMAP),&bNew);
C> TRACE(TEXT("new cx=%d cy=%d\n"),bNew.bmWidth,bNew.bmHeight);
C> {
C> Gdiplus::Bitmap bmpForSave(hNew,NULL);
C> CLSID bmpClsid ={0};
C> GetCodecClsid(L"image/bmp", &bmpClsid);
C> st= bmpForSave.Save(L"C:\\Test_GDI.bmp",&bmpClsid,NULL);
C> ASSERT(Gdiplus::Ok == st);
C> }
C>#endif
C> return hNew;
C>}
C>
C>Проблема в следующем: все масштабируется, но на выходе получаем обрезанный битмап по размеру исходного. То бишь зум видно невооруженным взглядом — все чики-поки. Но если масштаб больше 100 — тогда обрезается по размеру исходного битмапа. Если масштаб меньше 100 — то опять же, все масштабируется, результирующий битмап ессесна получается меньше исходого. Но опять же все по размеру исходного, просто с черным фоном.
C>Ни черта не понимаю уже. Что я не так то хоть делаю!?! Уже только что не перепробовал, не попеременял в коде. Ан фиг! Хелп, коллеги!
А если так?
g.SetClip(Gdiplus::Rect(0,0,cx,cy)) ;// не нужен
...
...
g.DrawImage(&bmpTemp,0,0);
Пробовал. Вообще не масштабирует это раз (я имею ввиду зум радостно пропадает), ну или в каких-то ранних пробах, все равно режется.
Факт на лицо:
1) до вызова ScaleTransform — GetVisibleClipBounds выдает корректный RECT — новый (ну например больше на нужное число пикселов)
2) после вызова ScaleTransform (т.е. сразу проверяем, следующей строкой) — GetVisibleClipBounds выдает уже старый RECT. Именно что по размеру исходого битмапа. Такое ощущение, что вызов ScaleTransform заставляет GDI+ что-то перерасчитать… Ну и результат налицо: масштаб есть, это просто видно глазами, но выдает он только часть, которая влезла в старый (меньший) RECT
3) Пробовал закомментировать ScaleTransform. Тогда нет зума. Рисуется 1 в 1. Мысли вроде такой: мол всякие преобразования я тебе задал (SetInterpolation, Quality и.тд.), а потом сказал рисуй с новой шириной\высотой (большЕго RECT), а GDI+ мол сама все натянет\растянет. Дык фиг! Опять же рисует в масштабе 1 к 1.
C>Пробовал. Вообще не масштабирует это раз (я имею ввиду зум радостно пропадает), ну или в каких-то ранних пробах, все равно режется.
C>Факт на лицо: C>1) до вызова ScaleTransform — GetVisibleClipBounds выдает корректный RECT — новый (ну например больше на нужное число пикселов)
C>2) после вызова ScaleTransform (т.е. сразу проверяем, следующей строкой) — GetVisibleClipBounds выдает уже старый RECT. Именно что по размеру исходого битмапа. Такое ощущение, что вызов ScaleTransform заставляет GDI+ что-то перерасчитать… Ну и результат налицо: масштаб есть, это просто видно глазами, но выдает он только часть, которая влезла в старый (меньший) RECT
C>3) Пробовал закомментировать ScaleTransform. Тогда нет зума. Рисуется 1 в 1. Мысли вроде такой: мол всякие преобразования я тебе задал (SetInterpolation, Quality и.тд.), а потом сказал рисуй с новой шириной\высотой (большЕго RECT), а GDI+ мол сама все натянет\растянет. Дык фиг! Опять же рисует в масштабе 1 к 1.
Посмотреть бы результат (исходная картинка, scale, результирующая)
ScaleTransform создает матрицу преобразования для всей сцены. Вероятно, масштабирование происходит вокруг центра битмапа, соответственно левый верхний угол смещается. Если так, то нужно юзать TranslateTransform.
MT>[/cut] MT>Посмотреть бы результат (исходная картинка, scale, результирующая) MT>ScaleTransform создает матрицу преобразования для всей сцены. Вероятно, масштабирование происходит вокруг центра битмапа, соответственно левый верхний угол смещается. Если так, то нужно юзать TranslateTransform.
Тут бинарник http://www.amlpages.com/Source/richedit_test.zip
При старте сразу вставляет битмапку в CRichEditView, и стартует диалог ресайза. Там можно поиграться. Изменяем ширину, софтина перерассчитывает длину и пытается создать новый битмап на основе старого отмасштабированного по новой длине и ширине. Ну и изменяет высоту, тогда все наоборот — вычисляет ширину на основе новой высоты и.т.д…
Здравствуйте, Carc, Вы писали:
C>Здравствуйте, MTimur, Вы писали:
MT>>[/cut] MT>>Посмотреть бы результат (исходная картинка, scale, результирующая) MT>>ScaleTransform создает матрицу преобразования для всей сцены. Вероятно, масштабирование происходит вокруг центра битмапа, соответственно левый верхний угол смещается. Если так, то нужно юзать TranslateTransform. C>Тут бинарник http://www.amlpages.com/Source/richedit_test.zip
C>При старте сразу вставляет битмапку в CRichEditView, и стартует диалог ресайза. Там можно поиграться. Изменяем ширину, софтина перерассчитывает длину и пытается создать новый битмап на основе старого отмасштабированного по новой длине и ширине. Ну и изменяет высоту, тогда все наоборот — вычисляет ширину на основе новой высоты и.т.д…
MT>Без всяких матриц преобразований.
Дык в том то и дело, что такие варианты с rect или c (x,y,cx,cy) я попробовал сразу. Благо они во всех интернетах мелькают. Но не получается
MT>ps как то так
MT>pps И да, судя по результату, я бы смотрел код, который копирует отмасштабированный битмап на окно, а не код ресайза.
Дык вроде нету там копирования. Там CStatic и ему делается CStatic::SetBitmap, суть простые обертки над WinAPI.
Ресайз идет в CDlgResize (это тот диалог, который она запускает при старте, он же и меню Pictures\Resize, но нужно чтобы в CRichEditView был выделен обьект).
Здравствуйте, Carc, Вы писали:
C>Дык в том то и дело, что такие варианты с rect или c (x,y,cx,cy) я попробовал сразу. Благо они во всех интернетах мелькают. Но не получается
Он работает, это точно.
C>Дык вроде нету там копирования. Там CStatic и ему делается CStatic::SetBitmap, суть простые обертки над WinAPI.
Ну ок. Не ты копируешь, а CStatic, суть одна.. Размер твоего CStatic cx*cy. Тогда:
1. Размер картинки 0.5(cx*cy). CStatic благополучно отрисовывает ее на своем DC, а все остальное оставляет как есть (черный BG).
2. Размер картинки 2*(cx*cy). CStatic отрисовывает ее на своем DC только то, что помещается в его окно. Результат — обрезан правый и нижний край.
Копируй картинку сам на DC контрола. Я бы прям на диалоге рисовал и обрамлял бы прямоугольником.
C>Код: http://www.amlpages.com/Source/richedit_test.rar, MFC, VS6. C>Ресайз идет в CDlgResize (это тот диалог, который она запускает при старте, он же и меню Pictures\Resize, но нужно чтобы в CRichEditView был выделен обьект).
MT>В результате, у меня все заработало. Весь файл после изменений DlgPicResize.cpp
СПАСИБО!
Заработало… Правда, я так и не понял, что ты нашаманил, чего я не шаманил. CopyImage + LR_CREATEDIBSECTION без ширины, высоты? А что оно дает?
Дело в том, что остальное (GDI+ варианты Draw с разными параметрами) я вроде как пробовал. Тем более что по всей сети были примеры как раз безо всяких ScaleTransform.
Но в том то, все и дело, что у меня то по любому не получалось.
MT>зы А вообще трэш какой-то. Это же не продакшн, да?
Ну конечно же нет. Тест. И соответственно говорящее название RichEdit_Test. Простенький проект, на котором гоняются только отдельные модули\классы, с конкретным функционалом и только!
Здравствуйте, CEMb, Вы писали:
CEM>Здравствуйте, Carc, Вы писали:
C>>Нужно отмасштабировать битмапку в памяти с заданными зумом.
CEM>если ещё актуально, могу дать свой код масштабирования картинки в памяти любого размера в любой размер с линейным выравниванием
Дафай! Все равно хочу досконально разобраться…
Здравствуйте, Carc, Вы писали: C>Дафай! Все равно хочу досконально разобраться…
Код
LPBYTE m_lpByte;// байтовые данные
SIZE m_size; //
// 1. только 32-битные картинки
// 2. делается растяжка сначала в одну сторону, потом в другую, поэтому красивого радиального выравнивания нету
// 3. используются кривые Безье 1-го порядка :)
Здравствуйте, Carc, Вы писали:
C>Заработало… Правда, я так и не понял, что ты нашаманил, чего я не шаманил.
Посмотри diff, проанализируй. Я вроде бы все изменения описал.
C>CopyImage + LR_CREATEDIBSECTION без ширины, высоты? А что оно дает?
bmp.bmWidth, bmp.bmHeight — это размер исходного битмапа, а копируемый битмап у тебя либо больше, либо меньше. Что получится в результате должно быть понятно.
Здравствуйте, MTimur, Вы писали:
MT>Здравствуйте, Carc, Вы писали:
C>>Заработало… Правда, я так и не понял, что ты нашаманил, чего я не шаманил. MT>Посмотри diff, проанализируй. Я вроде бы все изменения описал.
На кой дифф? Глаза же есть… Я в том смысле, что не уловил твоей мысли…
C>>CopyImage + LR_CREATEDIBSECTION без ширины, высоты? А что оно дает? MT>bmp.bmWidth, bmp.bmHeight — это размер исходного битмапа, а копируемый битмап у тебя либо больше, либо меньше. Что получится в результате должно быть понятно.
Дык вроде там CopyImage юзался для копирования исходного битмапа, который потом отрисовывается GDI+ со всякими вывертами (я пытался через ScaleTransfofm, в твоем коде уже просто DrawImage с заданной шириной\высотой).
Дык тут и непонятка: какая разница что я указываю при CopyImage исходного битмапа? Все равно что есть в исходном, то и скопируется — что задавай я ширину, высоту, что нет. Разве я не прав?
PS: я исходный копировал исключительно из-за стремных мыслей, что GDI+::Bitmap в деструкторе удалит HBITMAP полученный в конструкторе. А мне этот исходный Bitmap вроде как нужен был потом… На да эти стремки лишнее, HBITMAP деструктор не трогает.
Здравствуйте, Carc, Вы писали:
C>Дык вроде там CopyImage юзался для копирования исходного битмапа, который потом отрисовывается GDI+ со всякими вывертами (я пытался через ScaleTransfofm, в твоем коде уже просто DrawImage с заданной шириной\высотой).
cxDesired [in]
Type: int
The desired width, in pixels, of the image. If this is zero, then the returned image will have the same width as the original hImage.
cyDesired [in]
Type: int
The desired height, in pixels, of the image. If this is zero, then the returned image will have the same height as the original hImage.
Т.е. ты уменьшил/увеличил картинку, а потом отрисовал ее с исходным размером.
Здравствуйте, MTimur, Вы писали:
MT>Здравствуйте, Carc, Вы писали:
C>>Дык вроде там CopyImage юзался для копирования исходного битмапа, который потом отрисовывается GDI+ со всякими вывертами (я пытался через ScaleTransfofm, в твоем коде уже просто DrawImage с заданной шириной\высотой).
MT>Допустим исходный битмап имеет размер 100*100. MT>1. Из увеличенного битмапа размером 200*200 точек копируем область размером 100*100. Что получится? MT>2. Из уменьшенного битмапа размером 50*50 точек копируем область размером 100*100. Результат?
Ну ты не уловил… У меня там как было примерно…
1) Копирую исходный битмап ДО рисования. (сейчас я это убрал — ибо лишнее)
2) Создаю битмап в памяти, чтобы отрисоваться на нем. И выбираю его в Graphics в конструкторе.
3) Рисую исходный со новой шириной\высотой (как ты подсказал), или (как не работало) со ScaleTransform…
Т.е. улови, я не копирую результирующий битмап. Я копировал только исходный, и то это оказалось лишним, и сейчас я это убрал.