/******************************************************************************* * pvdisplay.cpp * * --------------------------------------------------------------------------- * Persistence of Vision Ray Tracer ('POV-Ray') version 3.7. * Copyright 1991-2013 Persistence of Vision Raytracer Pty. Ltd. * * POV-Ray is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * POV-Ray is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * --------------------------------------------------------------------------- * POV-Ray is based on the popular DKB raytracer version 2.12. * DKBTrace was originally written by David K. Buck. * DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins. * --------------------------------------------------------------------------- * $File: //depot/public/povray/3.x/windows/pvdisplay.cpp $ * $Revision: #1 $ * $Change: 6069 $ * $DateTime: 2013/11/06 11:59:40 $ * $Author: chrisc $ *******************************************************************************/ #define POVWIN_FILE #define DECLARE_TABLES #include #include "pvengine.h" #include "pvfrontend.h" #include "pvdisplay.h" #include "resource.h" #include "pvedit.h" #include "pvguiext.h" #include "pvdialog.h" #ifdef RTR_SUPPORT #include "rtrsupport.h" #endif // this must be the last file included #include "syspovdebug.h" namespace povwin { char PovLegacyRenderWinClass [] = CLASSNAMEPREFIX "LegacyRenderWinClass" ; int renderwin_xoffset ; int renderwin_yoffset ; int renderwin_left = CW_USEDEFAULT ; int renderwin_top = CW_USEDEFAULT ; bool MakeRenderwinActive ; unsigned renderwin_flags = 0 ; extern int screen_origin_x ; extern int screen_origin_y ; extern int virtual_screen_width ; extern int virtual_screen_height ; extern int render_bitmap_depth ; extern bool rendering_animation ; extern bool preserve_bitmap ; extern bool first_frame ; extern bool RenderwinIsChild ; extern bool main_window_hidden ; extern bool running_demo ; extern bool demo_mode ; extern bool benchmark_mode; extern bool temp_render_region ; extern bool UseAlpha ; extern char RegionStr [128] ; extern char TempRegionStr [128] ; extern char command_line [_MAX_PATH * 3] ; extern HWND main_window ; extern HWND toolbar_cmdline ; extern HMENU hPopupMenus ; extern unsigned screen_width ; extern unsigned screen_height ; extern HPALETTE hPalApp ; extern HPALETTE hPalBitmap ; extern WINDOWPLACEMENT mainwin_placement ; } //////////////////////////////////////////////////////////////////////////////////////// // // class WinLegacyDisplay // //////////////////////////////////////////////////////////////////////////////////////// namespace pov_frontend { using namespace povwin; shared_ptr gDisplay; BitmapInfo WinLegacyDisplay::m_BitmapTemplate; WinLegacyDisplay::WinLegacyDisplay(unsigned int w, unsigned int h, GammaCurvePtr gamma, vfeSession *session, bool visible) : WinDisplay(w, h, gamma, session, visible) { m_BitmapSurface = NULL; m_LastY = 0; m_BytesPerLine = 0; m_ErrorBitmap = NULL; m_RBand = 0; m_LastMouseX = -1; m_LastMouseY = -1; m_Depth8Bit = render_bitmap_depth <= 8 ; m_MaxWidth = 0; m_MaxHeight = 0; m_Rendering = true; m_Initialised = false; } WinLegacyDisplay::~WinLegacyDisplay() { Close(); } bool WinLegacyDisplay::TakeOver(WinDisplay *display) { WinLegacyDisplay *p = dynamic_cast(display); if (p == NULL) return (false); if (m_Initialised == true) return (false); if (m_Depth8Bit != p->m_Depth8Bit) return (false); if ((GetWidth() != p->GetWidth()) || (GetHeight() != p->GetHeight())) return (false); // TODO: protect this with a mutex m_Bitmap = p->m_Bitmap; m_BitmapSurface = p->m_BitmapSurface; m_Handle = p->m_Handle; m_RBand = p->m_RBand; m_RB1 = p->m_RB1; m_RB2 = p->m_RB2; m_LastMouseX = p->m_LastMouseX; m_LastMouseY = p->m_LastMouseY; m_BytesPerLine = p->m_BytesPerLine; m_Depth8Bit = p->m_Depth8Bit; m_MaxWidth = p->m_MaxWidth; m_MaxHeight = p->m_MaxHeight; m_Initialised = true; SetWindowLongPtr (m_Handle, 0, LONG_PTR (this)); p->m_Handle = NULL ; p->m_BitmapSurface = NULL; return (true) ; } bool WinLegacyDisplay::CreateRenderWindow (void) { int width ; int height ; RECT rect ; unsigned flags ; if (m_Handle != NULL) DestroyWindow (m_Handle) ; m_Handle = NULL ; renderwin_xoffset = renderwin_yoffset = 0 ; renderwin_flags = 0 ; rect.left = 0 ; rect.top = 0 ; rect.right = GetWidth() ; rect.bottom = GetHeight() ; flags = WS_OVERLAPPEDWINDOW ; AdjustWindowRectEx (&rect, flags, false, 0) ; // left and top will probably be negative width = m_MaxWidth = rect.right - rect.left ; height = m_MaxHeight = rect.bottom - rect.top ; if (width > screen_width - 64) width = screen_width - 64 ; if (height > screen_height - 48) height = screen_height - 48 ; if (renderwin_left < screen_origin_x) renderwin_left = screen_origin_x ; if (renderwin_top < screen_origin_y) renderwin_top = screen_origin_y ; if (renderwin_left + width > virtual_screen_width + screen_origin_x) renderwin_left = virtual_screen_width - width + screen_origin_x ; if (renderwin_top + height > virtual_screen_height + screen_origin_y) renderwin_top = virtual_screen_height - height + screen_origin_y ; if (m_BitmapSurface != NULL) { free (m_BitmapSurface) ; m_BitmapSurface = NULL ; } m_Bitmap = m_BitmapTemplate ; m_Bitmap.header.biSize = sizeof (BITMAPINFOHEADER) ; m_Bitmap.header.biWidth = GetWidth() ; m_Bitmap.header.biHeight = GetHeight() ; m_Bitmap.header.biPlanes = 1 ; m_Bitmap.header.biBitCount = m_Depth8Bit ? 8 : 24 ; m_Bitmap.header.biCompression = BI_RGB ; if (m_Depth8Bit) { // round out the bits per line to a multiple of four m_BytesPerLine = (GetWidth() + 3) & ~3 ; m_Bitmap.header.biClrUsed = 256 ; } else { m_BytesPerLine = (GetWidth() * 3 + 3) & ~3 ; m_Bitmap.header.biClrUsed = 0 ; } m_Bitmap.header.biSizeImage = m_BytesPerLine * GetHeight() ; m_Bitmap.header.biClrImportant = 0 ; m_BitmapSurface = new unsigned char [m_Bitmap.header.biSizeImage]; if (m_BitmapSurface == NULL) return (false); Clear () ; if ((m_Handle = CreateWindowEx (0, PovLegacyRenderWinClass, "POV-Ray", flags | renderwin_flags, renderwin_left, renderwin_top, width, height, RenderwinIsChild ? main_window : NULL, NULL, hInstance, NULL)) == NULL) { return (false) ; } // store a pointer to this instance for the window procedure SetWindowLongPtr (m_Handle, 0, LONG_PTR (this)); if (width < m_MaxWidth) { SetScrollRange (m_Handle, SB_HORZ, 0, GetWidth() - width, false) ; SetScrollPos (m_Handle, SB_HORZ, 0, true) ; } if (height < m_MaxHeight) { SetScrollRange (m_Handle, SB_VERT, 0, GetHeight() - height, false) ; SetScrollPos (m_Handle, SB_VERT, 0, true) ; } if (!main_window_hidden) { mainwin_placement.length = sizeof (WINDOWPLACEMENT) ; GetWindowPlacement (main_window, &mainwin_placement) ; if (mainwin_placement.showCmd != SW_SHOWMINIMIZED) { // we don't set SWP_SHOWWINDOW at this point flags = SWP_NOMOVE | SWP_NOSIZE | SWP_NOCOPYBITS | SWP_NOACTIVATE ; if ((GetForegroundWindow () == main_window) && RenderwinIsChild) flags &= ~SWP_NOACTIVATE ; if (MakeRenderwinActive) flags &= ~SWP_NOACTIVATE ; SetWindowPos (m_Handle, main_window, 0, 0, 0, 0, flags) ; } else renderwin_flags = WS_MINIMIZE ; } SetClassLongPtr (m_Handle, GCLP_HCURSOR, (LONG_PTR) LoadCursor (NULL, m_Rendering ? IDC_ARROW : IDC_CROSS)) ; InvalidateRect (m_Handle, NULL, false) ; return (true) ; } void WinLegacyDisplay::Initialise() { if (m_Initialised) return; m_Initialised = true; // we do this so that the window is created in the context of the main UI thread if (SendMessage (main_window, CREATE_RENDERWIN_MESSAGE, 0, LPARAM (this)) == false) return ; if (m_VisibleOnCreation) Show(); ExternalEvent (EventDisplayInit, MAKELONG (GetWidth(), GetHeight())) ; } void WinLegacyDisplay::Close() { if (m_Handle != NULL) { // we can't destroy the window ourselves as we most likely aren't the owning thread SendMessage (main_window, WM_COMMAND, CM_RENDERDESTROY, (LPARAM) m_Handle); m_Handle = NULL ; PVEnableMenuItem (CM_RENDERSHOW, MF_ENABLED) ; PVEnableMenuItem (CM_RENDERCLOSE, MF_GRAYED) ; } if (m_BitmapSurface != NULL) { delete[] m_BitmapSurface ; m_BitmapSurface = NULL ; PVEnableMenuItem (CM_RENDERSHOW, MF_GRAYED) ; } if (m_ErrorBitmap != NULL) { DeleteObject (m_ErrorBitmap) ; m_ErrorBitmap = NULL ; } } void WinLegacyDisplay::Show() { if (gDisplay.get() != this) gDisplay = m_Session->GetDisplay(); if (m_Handle != NULL) { ShowWindow (m_Handle, SW_SHOW) ; PVEnableMenuItem (CM_RENDERSHOW, MF_GRAYED) ; PVEnableMenuItem (CM_RENDERCLOSE, MF_ENABLED) ; } } void WinLegacyDisplay::Hide() { if (m_Handle != NULL) { ShowWindow (m_Handle, SW_HIDE) ; PVEnableMenuItem (CM_RENDERSHOW, MF_ENABLED) ; PVEnableMenuItem (CM_RENDERCLOSE, MF_GRAYED) ; } } inline void WinLegacyDisplay::SetPixel (unsigned int x, unsigned int y, const pov_frontend::Display::RGBA8 *colour) { if (m_Depth8Bit) { unsigned char *p = m_BitmapSurface + (m_Bitmap.header.biHeight - 1 - y) * m_BytesPerLine + x ; unsigned char dither = dither8x8 [((x & 7) << 3) | (y & 7)] ; *p = 20 + div51 [colour->red] + (mod51 [colour->red] > dither) + mul6 [div51 [colour->green] + (mod51 [colour->green] > dither)] + mul36 [div51 [colour->blue] + (mod51 [colour->blue] > dither)] ; } else { unsigned char *p = m_BitmapSurface + (m_Bitmap.header.biHeight - 1 - y) * m_BytesPerLine + x * 3 ; if (pov_frontend::UseAlpha && colour->alpha < 255) { uint backColor = (x & 8) == (y & 8) ? (uint) (255 - colour->alpha) * 0xff : (uint) (255 - colour->alpha) * 0xc0 ; *p++ = (unsigned char) (((uint) colour->blue * colour->alpha + backColor) / 255) ; *p++ = (unsigned char) (((uint) colour->green * colour->alpha + backColor) / 255) ; *p++ = (unsigned char) (((uint) colour->red * colour->alpha + backColor) / 255) ; } else { *p++ = colour->blue ; *p++ = colour->green ; *p++ = colour->red ; } } } inline void WinLegacyDisplay::SetPixel (unsigned int x, unsigned int y, unsigned char colour) { if (m_Depth8Bit) { unsigned char *p = m_BitmapSurface + (m_Bitmap.header.biHeight - 1 - y) * m_BytesPerLine + x ; unsigned char dither = dither8x8 [((x & 7) << 3) | (y & 7)] ; *p = 20 + div51 [colour] + (mod51 [colour] > dither) + mul6 [div51 [colour] + (mod51 [colour] > dither)] + mul36 [div51 [colour] + (mod51 [colour] > dither)] ; } else { unsigned char *p = m_BitmapSurface + (m_Bitmap.header.biHeight - 1 - y) * m_BytesPerLine + x * 3 ; *p++ = colour ; *p++ = colour ; *p++ = colour ; } } void WinLegacyDisplay::DrawPixel(unsigned int x, unsigned int y, const RGBA8& colour) { unsigned char Red = colour.red ; unsigned char Green = colour.green ; unsigned char Blue = colour.blue ; unsigned char Alpha = colour.alpha ; ExternalDisplayPlot (x, y, Red, Green, Blue, Alpha) ; if ((int) x == -1 || (int) y == -1) { m_LastY = 0 ; return ; } if (x >= GetWidth() || y >= GetHeight()) return ; if (m_BitmapSurface == NULL) return ; InvalidatePixelBlock (0, m_LastY, GetWidth(), m_LastY) ; m_LastY = y ; SetPixel (x, y, &colour) ; } void WinLegacyDisplay::DrawRectangleFrame(unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2, const RGBA8& colour) { if (m_BitmapSurface == NULL) return ; // try to keep the lines coherent to avoid unnecessary calls to InvalidatePixelBlock() for (unsigned int x = x1; x <= x2; x++) SetPixel(x, y1, &colour); for (unsigned int x = x1; x <= x2; x++) SetPixel(x, y2, &colour); for (unsigned int y = y1; y <= y2; y++) { SetPixel(x1, y, &colour); SetPixel(x2, y, &colour); } InvalidatePixelBlock (x1, y1, x2, y1) ; InvalidatePixelBlock (x1, y1, x1, y2) ; InvalidatePixelBlock (x1, y2, x2, y2) ; InvalidatePixelBlock (x2, y1, x2, y2) ; } void WinLegacyDisplay::DrawFilledRectangle(unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2, const RGBA8& colour) { if (GetWidth() == 0 || GetHeight() == 0) return; if (x2 >= GetWidth()) x2 = GetWidth() - 1 ; if (y2 >= GetHeight()) y2 = GetHeight() - 1 ; if (x1 > x2 || y1 > y2) return ; unsigned char Red = colour.red ; unsigned char Green = colour.green ; unsigned char Blue = colour.blue ; unsigned char Alpha = colour.alpha ; ExternalDisplayPlotRect (x1, y1, x2, y2, Red, Green, Blue, Alpha) ; if (m_BitmapSurface == NULL) return ; for (unsigned int y = y1 ; y <= y2; y++) for (unsigned int x = x1; x <= x2; x++) SetPixel (x, y, &colour) ; InvalidatePixelBlock (x1, y1, x2, y2) ; } void WinLegacyDisplay::DrawPixelBlock(unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2, const RGBA8 *colour) { #ifdef RTR_SUPPORT if (m_Session->GetRealTimeRaytracing() == true) NextFrame(); #endif if (m_BitmapSurface == NULL) return ; if (x1 >= GetWidth() || x2 >= GetWidth()) return ; if (y1 >= GetHeight() || y2 >= GetHeight()) return ; ExternalDisplayPlotBlock (x1, y1, x2, y2, colour) ; if (gDisplay.get() != this) gDisplay = m_Session->GetDisplay(); for (unsigned int y = y1 ; y <= y2; y++) for (unsigned int x = x1; x <= x2; x++) SetPixel (x, y, colour++) ; if (m_Session->GetRealTimeRaytracing() == true) { // TODO: move painting code into common routine shared with message handler RECT rect; int dest_width ; int dest_height ; int dest_xoffset ; int dest_yoffset ; HDC hdc = GetDC (m_Handle); if (hdc == NULL) return ; int oldMode = SetStretchBltMode (hdc, STRETCH_DELETESCANS) ; if (hPalApp) { SelectPalette (hdc, hPalApp, false) ; RealizePalette (hdc) ; } GetClientRect (m_Handle, &rect) ; if (IsZoomed(m_Handle)) { double aspect_ratio = (double) GetWidth() / GetHeight() ; double screen_aspect = (double) rect.right / rect.bottom ; if (aspect_ratio >= screen_aspect) { dest_width = rect.right ; dest_height = (int) ((double) rect.right / aspect_ratio) ; } else { dest_width = (int) ((double) rect.bottom * aspect_ratio) ; dest_height = rect.bottom ; } dest_xoffset = (rect.right - dest_width) / 2 ; dest_yoffset = (rect.bottom - dest_height) / 2 ; if (dest_width < rect.right) { BitBlt (hdc, 0, 0, dest_xoffset + 1, rect.bottom, NULL, 0, 0, BLACKNESS) ; BitBlt (hdc, dest_width + dest_xoffset, 0, dest_xoffset + 1, rect.bottom + 1, NULL, 0, 0, BLACKNESS) ; } if (dest_height < rect.bottom) { BitBlt (hdc, 0, 0, rect.right, dest_yoffset + 1, NULL, 0, 0, BLACKNESS) ; BitBlt (hdc, 0, dest_height + dest_yoffset, rect.right, dest_yoffset + 1, NULL, 0, 0, BLACKNESS) ; } } else { dest_xoffset = -renderwin_xoffset ; dest_yoffset = -renderwin_yoffset ; dest_width = GetWidth() ; dest_height = GetHeight() ; GetClientRect (m_Handle, &rect) ; if (rect.right > dest_width) BitBlt (hdc, dest_width, 0, rect.right - dest_width, rect.bottom, NULL, 0, 0, BLACKNESS) ; if (rect.bottom > dest_height) BitBlt (hdc, 0, dest_height, rect.right, rect.bottom - dest_height, NULL, 0, 0, BLACKNESS) ; } StretchDIBits (hdc, dest_xoffset, dest_yoffset, dest_width, dest_height, 0, 0, m_Bitmap.header.biWidth, m_Bitmap.header.biHeight, m_BitmapSurface, (LPBITMAPINFO) &m_Bitmap, DIB_RGB_COLORS, SRCCOPY); SetStretchBltMode (hdc, oldMode) ; ReleaseDC (m_Handle, hdc); } else InvalidatePixelBlock (x1, y1, x2, y2) ; } void WinLegacyDisplay::InvalidatePixelBlock(unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2) { if (m_Handle != NULL && !IsIconic (m_Handle)) { RECT rect ; if (IsZoomed (m_Handle)) { int dest_width ; int dest_height ; GetClientRect (m_Handle, &rect) ; double aspect_ratio = (double) GetWidth() / GetHeight() ; double screen_aspect = (double) rect.right / rect.bottom ; if (aspect_ratio >= screen_aspect) { dest_width = rect.right ; dest_height = (int) ((double) rect.right / aspect_ratio) ; } else { dest_width = (int) ((double) rect.bottom * aspect_ratio) ; dest_height = rect.bottom ; } int x = dest_width * x1 / GetWidth() ; int y = dest_height * y1 / GetHeight() ; int width = (dest_width * (x2 - x1 + 1) + GetWidth() - 1) / GetWidth() ; int height = (dest_height * (y2 - y1 + 1) + GetHeight() - 1) / GetHeight() ; int dest_xoffset = (rect.right - dest_width) / 2 ; int dest_yoffset = (rect.bottom - dest_height) / 2 ; rect.left = x + dest_xoffset ; rect.top = y + dest_yoffset ; rect.right = rect.left + width + 1 ; rect.bottom = rect.top + height + 1 ; } else { rect.left = x1 - renderwin_xoffset ; rect.top = y1 - renderwin_yoffset ; rect.right = x2 + 1 ; rect.bottom = y2 + 1 ; } InvalidateRect (m_Handle, &rect, false) ; } } void WinLegacyDisplay::Clear() { if (m_BitmapSurface == NULL) return ; if (m_Depth8Bit) { for (int y = 0 ; y < m_Bitmap.header.biHeight ; y++) { for (int x = 0 ; x < m_Bitmap.header.biWidth ; x++) { int colour = (x & 8) == (y & 8) ? 0xff : 0xc0 ; unsigned char *p = m_BitmapSurface + (m_Bitmap.header.biHeight - 1 - y) * m_BytesPerLine + x ; unsigned char dither = dither8x8 [((x & 7) << 3) | (y & 7)] ; *p = 20 + div51 [colour] + (mod51 [colour] > dither) + mul6 [div51 [colour] + (mod51 [colour] > dither)] + mul36 [div51 [colour] + (mod51 [colour] > dither)] ; } } } else { for (int y = 0 ; y < m_Bitmap.header.biHeight ; y++) { unsigned char *p = m_BitmapSurface + (m_Bitmap.header.biHeight - 1 - y) * m_BytesPerLine ; for (int x = 0 ; x < m_Bitmap.header.biWidth ; x += 8) { if (x + 8 <= m_Bitmap.header.biWidth) { memset (p, (x & 8) == (y & 8) ? 0xff : 0xc0, 24) ; p += 24 ; } else memset (p, (x & 8) == (y & 8) ? 0xff : 0xc0, (m_Bitmap.header.biWidth - x) * 3) ; } } } if (m_Handle != NULL) InvalidateRect (m_Handle, NULL, false) ; } void WinLegacyDisplay::SetRenderState(bool IsRendering) { if (m_Rendering != IsRendering) { m_Rendering = IsRendering; SetClassLongPtr (m_Handle, GCLP_HCURSOR, (LONG_PTR) LoadCursor (NULL, m_Rendering ? IDC_ARROW : IDC_CROSS)) ; } } LRESULT WinLegacyDisplay::WindowProc (UINT message, WPARAM wParam, LPARAM lParam) { int dest_width ; int dest_height ; int dest_xoffset ; int dest_yoffset ; int oldMode ; int width; int height; HDC hdc ; HDC hdcMemory ; RECT rect ; HPEN hpen ; char str [512] ; char *s ; bool zoomed = IsZoomed (m_Handle) != 0 ; POINT pt ; POINT pts [5] ; short x1 ; short y1 ; short x2 ; short y2 ; HBITMAP oldBmp ; MINMAXINFO *pInfo ; PAINTSTRUCT ps ; // only initialize these variables if we need to switch (message) { case WM_LBUTTONDOWN : case WM_LBUTTONUP : case WM_MOUSEMOVE : case WM_PAINT : GetClientRect (m_Handle, &rect) ; if (zoomed) { double aspect_ratio = (double) GetWidth() / GetHeight() ; double screen_aspect = (double) rect.right / rect.bottom ; if (aspect_ratio >= screen_aspect) { dest_width = rect.right ; dest_height = (int) ((double) rect.right / aspect_ratio) ; } else { dest_width = (int) ((double) rect.bottom * aspect_ratio) ; dest_height = rect.bottom ; } dest_xoffset = (rect.right - dest_width) / 2 ; dest_yoffset = (rect.bottom - dest_height) / 2 ; } else { dest_xoffset = dest_yoffset = 0 ; dest_width = rect.right ; dest_height = rect.bottom ; } } switch (message) { case WM_SETFOCUS : // this will handle the annoying situation where another window is // in the z-order below the render window but above the main window. if (RenderwinIsChild) SetWindowPos (main_window, m_Handle, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE) ; break ; case WM_CHAR : // if any character is pressed, pass focus to the main window EditSetFocus () ; return (0) ; case WM_CLOSE : if (!running_demo && !demo_mode && !benchmark_mode) FeatureNotify ("RenderwinClose", "POV-Ray - Render Window", "If you find that the render window gets in your way during " "or after rendering, there are a number of options to control it.\n\n" "Press F1 to learn more.", "Render Window Menu", true) ; SendMessage (main_window, WM_COMMAND, CM_RENDERCLOSE, 0L) ; return (0) ; case WM_COMMAND : if (handle_main_command (wParam, lParam)) return (0) ; break ; case WM_LBUTTONDOWN : if (m_Rendering) { FlashWindow (m_Handle, TRUE) ; MessageBeep (-1) ; FlashWindow (m_Handle, FALSE) ; return (0) ; } if (GetWidth() < 2 || GetHeight() < 2) return (0) ; m_RBand = 1 ; if ((wParam & MK_SHIFT) != 0) m_RBand++ ; if (zoomed) { m_RB1.x = MAX (MIN (LOWORD (lParam), dest_xoffset + dest_width - 1), dest_xoffset) ; m_RB1.y = MAX (MIN (HIWORD (lParam), dest_yoffset + dest_height - 1), dest_yoffset) ; } else { m_RB1.x = LOWORD (lParam) ; m_RB1.y = HIWORD (lParam) ; } m_RB2 = m_RB1 ; SetCapture (m_Handle) ; return (0) ; case WM_LBUTTONUP : case WM_MOUSEMOVE : x2 = LOWORD (lParam) ; y2 = HIWORD (lParam) ; if (message == WM_MOUSEMOVE) if (x2 == m_LastMouseX && y2 == m_LastMouseY) return (0) ; m_LastMouseX = x2 ; m_LastMouseY = y2 ; if (m_RBand) { hdc = GetDC (m_Handle) ; pts [0] = m_RB1 ; pts [1] = m_RB1 ; pts [1].x = m_RB2.x ; pts [2] = m_RB2 ; pts [3] = m_RB2 ; pts [3].x = m_RB1.x ; pts [4] = m_RB1 ; hpen = CreatePen (PS_DOT, 1, RGB (192, 192, 192)) ; hpen = (HPEN) SelectObject (hdc, hpen) ; oldMode = SetROP2 (hdc, R2_XORPEN) ; Polyline (hdc, pts, 5) ; if (zoomed) { m_RB2.x = x2 = MAX (MIN (x2, dest_xoffset + dest_width - 1), dest_xoffset) ; m_RB2.y = y2 = MAX (MIN (y2, dest_yoffset + dest_height - 1), dest_yoffset) ; x2 -= dest_xoffset ; y2 -= dest_yoffset ; x2 = MulDivNoRound (x2, GetWidth(), dest_width) ; y2 = MulDivNoRound (y2, GetHeight(), dest_height) ; // the values in m_RB1 have already been clipped x1 = m_RB1.x - dest_xoffset ; y1 = m_RB1.y - dest_yoffset ; x1 = MulDivNoRound (x1, GetWidth(), dest_width) ; y1 = MulDivNoRound (y1, GetHeight(), dest_height) ; } else { m_RB2.x = x2 = MAX (MIN (x2, dest_width - 1), 0) ; m_RB2.y = y2 = MAX (MIN (y2, dest_height - 1), 0) ; x2 += renderwin_xoffset ; y2 += renderwin_yoffset ; x1 = m_RB1.x + renderwin_xoffset ; y1 = m_RB1.y + renderwin_yoffset ; } if (x1 > x2) x1 ^= x2 ^= x1 ^= x2 ; if (y1 > y2) y1 ^= y2 ^= y1 ^= y2 ; if (message != WM_LBUTTONUP) { sprintf (str, "%d,%d - %d,%d", x1, y1, x2, y2) ; SetWindowText (m_Handle, str) ; pts [1].x = m_RB2.x ; pts [2] = m_RB2 ; pts [3] = m_RB2 ; pts [3].x = m_RB1.x ; Polyline (hdc, pts, 5) ; SetROP2 (hdc, oldMode) ; DeleteObject (SelectObject (hdc, hpen)) ; ReleaseDC (m_Handle, hdc) ; } else { ReleaseCapture () ; SetROP2 (hdc, oldMode) ; DeleteObject (SelectObject (hdc, hpen)) ; ReleaseDC (m_Handle, hdc) ; SetWindowText (m_Handle, "Render Window") ; if ((x2 - x1 > 2) && (y2 - y1 > 2)) { sprintf (str, "Selection is %d,%d to %d,%d\n\n", x1, y1, x2, y2) ; if (m_RBand == 1) { m_RBand = 0 ; strcat (str, "Press OK to render this region now.\n") ; strcat (str, "(You may shift-drag to set a permanent region next time).") ; if (MessageBox (main_window, str, "Render region", MB_OKCANCEL) == IDOK) { if (EditSaveModified (NULL) == 0) return (true) ; if (RegionStr [0]) { if ((s = strstr (command_line, RegionStr)) != NULL) strcpy (s, s + strlen (RegionStr)) ; else if ((s = strstr (command_line, RegionStr + 1)) != NULL) strcpy (s, s + strlen (RegionStr) - 1) ; SendMessage (toolbar_cmdline, WM_SETTEXT, 0, (LPARAM) command_line) ; RegionStr [0] = '\0' ; } sprintf (RegionStr, " +sc%f +sr%f +ec%f +er%f", (float) x1 / (GetWidth() - 1), (float) y1 / (GetHeight() - 1), (float) x2 / (GetWidth() - 1), (float) y2 / (GetHeight() - 1)) ; temp_render_region = true ; start_rendering (false) ; temp_render_region = false ; } } else { m_RBand = 0 ; strcat (str, "Press OK to append this region to the command-line.\n") ; if (MessageBox (main_window, str, "Render region", MB_OKCANCEL) == IDOK) { if (RegionStr [0]) { if ((s = strstr (command_line, RegionStr)) != NULL) strcpy (s, s + strlen (RegionStr)) ; else if ((s = strstr (command_line, RegionStr + 1)) != NULL) strcpy (s, s + strlen (RegionStr) - 1) ; SendMessage (toolbar_cmdline, WM_SETTEXT, 0, (LPARAM) command_line) ; } sprintf (TempRegionStr, " +sc%f +sr%f +ec%f +er%f", (float) x1 / (GetWidth() - 1), (float) y1 / (GetHeight() - 1), (float) x2 / (GetWidth() - 1), (float) y2 / (GetHeight() - 1)) ; PostMessage (main_window, WM_COMMAND, CM_COMMANDLINE, 0) ; } } } else m_RBand = 0 ; } } else { if (m_Rendering == false) { width = GetWidth(); height = GetHeight(); if (width > 1 && height > 1) { x2 += renderwin_xoffset - dest_xoffset ; y2 += renderwin_yoffset - dest_yoffset ; if (zoomed) { x2 = MulDivNoRound (x2, width, dest_width) ; y2 = MulDivNoRound (y2, height, dest_height) ; x2 = MAX (MIN (x2, width - 1), 0) ; y2 = MAX (MIN (y2, height - 1), 0) ; } sprintf (str, "%d,%d [%.3f,%.3f]", x2, y2, (float) x2 / (width - 1), (float) y2 / (height - 1)) ; SetWindowText (m_Handle, str) ; } } } return (0) ; case WM_RBUTTONDOWN : if (hPopupMenus != NULL) { pt.x = LOWORD (lParam) ; pt.y = HIWORD (lParam) ; ClientToScreen (m_Handle, &pt) ; TrackPopupMenu (GetSubMenu (hPopupMenus, 1), TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, 0, main_window, NULL) ; } return (0) ; case WM_GETMINMAXINFO : if (zoomed) break ; pInfo = (MINMAXINFO *) lParam ; pInfo->ptMaxTrackSize.x = m_MaxWidth ; pInfo->ptMaxTrackSize.y = m_MaxHeight ; break ; case WM_MOVE : if (m_Handle == NULL) break ; if (!IsIconic (m_Handle) && !zoomed) { GetWindowRect (m_Handle, &rect) ; renderwin_left = rect.left ; renderwin_top = rect.top ; } return (0) ; case WM_SIZE : if (m_Handle == NULL) break ; switch (wParam) { case SIZE_MINIMIZED : renderwin_flags = WS_MINIMIZE ; return (0) ; case SIZE_MAXIMIZED : renderwin_flags = WS_MAXIMIZE ; SetScrollRange (m_Handle, SB_HORZ, 0, 0, true) ; SetScrollRange (m_Handle, SB_VERT, 0, 0, true) ; PovInvalidateRect (m_Handle, NULL, false) ; UpdateWindow (m_Handle) ; SetClassLongPtr (m_Handle, GCLP_HCURSOR, (LONG_PTR) LoadCursor (NULL, m_Rendering ? IDC_ARROW : IDC_CROSS)) ; return (0) ; case SIZE_RESTORED : renderwin_flags = 0 ; PovInvalidateRect (m_Handle, NULL, false) ; UpdateWindow (m_Handle) ; break ; default : return (0) ; } // to get here we must be handling SIZE_RESTORED. GetWindowRect (m_Handle, &rect) ; width = rect.right - rect.left; height = rect.bottom - rect.top; // one problem we have here is that if we create one scroll bar, it takes away some of the client // area of the other direction (i.e. if we create a scroll bar for the X direction, it takes away // some of the Y client area). therefore we should create a scroll bar for that direction also. if (width < m_MaxWidth || height < m_MaxHeight) { if (renderwin_xoffset >= GetWidth() - LOWORD (lParam)) renderwin_xoffset = GetWidth() - LOWORD (lParam) ; SetScrollRange (m_Handle, SB_HORZ, 0, GetWidth() - LOWORD (lParam), false) ; SetScrollPos (m_Handle, SB_HORZ, renderwin_xoffset, true) ; if (renderwin_yoffset >= GetHeight() - HIWORD (lParam)) renderwin_yoffset = GetHeight() - HIWORD (lParam) ; SetScrollRange (m_Handle, SB_VERT, 0, GetHeight() - HIWORD (lParam), false) ; SetScrollPos (m_Handle, SB_VERT, renderwin_yoffset, true) ; } else { renderwin_xoffset = 0 ; renderwin_yoffset = 0 ; SetScrollRange (m_Handle, SB_VERT, 0, 0, true) ; SetScrollRange (m_Handle, SB_HORZ, 0, 0, true) ; } return (0) ; case WM_VSCROLL : GetClientRect (m_Handle, &rect) ; switch (LOWORD (wParam)) { case SB_LINEDOWN : if (renderwin_yoffset >= GetHeight() - rect.bottom) break ; SetScrollRange (m_Handle, SB_VERT, 0, GetHeight() - rect.bottom, false) ; SetScrollPos (m_Handle, SB_VERT, ++renderwin_yoffset, true) ; ScrollWindow (m_Handle, 0, -1, NULL, NULL) ; break ; case SB_LINEUP : if (renderwin_yoffset == 0) break ; SetScrollRange (m_Handle, SB_VERT, 0, GetHeight() - rect.bottom, false) ; SetScrollPos (m_Handle, SB_VERT, --renderwin_yoffset, true) ; ScrollWindow (m_Handle, 0, 1, NULL, NULL) ; break ; case SB_PAGEDOWN : renderwin_yoffset += rect.bottom ; if (renderwin_yoffset > GetHeight() - rect.bottom) renderwin_yoffset = GetHeight() - rect.bottom ; SetScrollPos (m_Handle, SB_VERT, renderwin_yoffset, true) ; PovInvalidateRect (m_Handle, NULL, false) ; break ; case SB_PAGEUP : renderwin_yoffset -= rect.bottom ; if (renderwin_yoffset < 0) renderwin_yoffset = 0 ; SetScrollPos (m_Handle, SB_VERT, renderwin_yoffset, true) ; PovInvalidateRect (m_Handle, NULL, false) ; break ; case SB_THUMBPOSITION : case SB_THUMBTRACK : renderwin_yoffset = HIWORD (wParam) ; SetScrollPos (m_Handle, SB_VERT, renderwin_yoffset, true) ; PovInvalidateRect (m_Handle, NULL, false) ; break ; } return (0) ; case WM_HSCROLL : GetClientRect (m_Handle, &rect) ; switch (LOWORD (wParam)) { case SB_LINERIGHT : if (renderwin_xoffset >= GetWidth() - rect.right) break ; SetScrollRange (m_Handle, SB_HORZ, 0, GetWidth() - rect.right, false) ; SetScrollPos (m_Handle, SB_HORZ, ++renderwin_xoffset, true) ; ScrollWindow (m_Handle, -1, 0, NULL, NULL) ; break ; case SB_LINELEFT : if (renderwin_xoffset == 0) break ; SetScrollRange (m_Handle, SB_HORZ, 0, GetWidth() - rect.right, false) ; SetScrollPos (m_Handle, SB_HORZ, --renderwin_xoffset, true) ; ScrollWindow (m_Handle, 1, 0, NULL, NULL) ; break ; case SB_PAGERIGHT : renderwin_xoffset += rect.right ; if (renderwin_xoffset > GetWidth() - rect.right) renderwin_xoffset = GetWidth() - rect.right ; SetScrollPos (m_Handle, SB_HORZ, renderwin_xoffset, true) ; PovInvalidateRect (m_Handle, NULL, false) ; break ; case SB_PAGELEFT : renderwin_xoffset -= rect.right ; if (renderwin_xoffset < 0) renderwin_xoffset = 0 ; SetScrollPos (m_Handle, SB_HORZ, renderwin_xoffset, true) ; PovInvalidateRect (m_Handle, NULL, false) ; break ; case SB_THUMBPOSITION : case SB_THUMBTRACK : renderwin_xoffset = HIWORD (wParam) ; SetScrollPos (m_Handle, SB_HORZ, renderwin_xoffset, true) ; PovInvalidateRect (m_Handle, NULL, false) ; break ; } return (0) ; case WM_PAINT : hdc = BeginPaint (m_Handle, &ps) ; if (IsIconic (m_Handle)) { EndPaint (m_Handle, &ps) ; return (0) ; } oldMode = SetStretchBltMode (hdc, STRETCH_DELETESCANS) ; if (hPalApp) { SelectPalette (hdc, hPalApp, false) ; RealizePalette (hdc) ; } if (zoomed) { GetClientRect (m_Handle, &rect) ; if (dest_width < rect.right) { BitBlt (hdc, 0, 0, dest_xoffset + 1, rect.bottom, NULL, 0, 0, BLACKNESS) ; BitBlt (hdc, dest_width + dest_xoffset, 0, dest_xoffset + 1, rect.bottom + 1, NULL, 0, 0, BLACKNESS) ; } if (dest_height < rect.bottom) { BitBlt (hdc, 0, 0, rect.right, dest_yoffset + 1, NULL, 0, 0, BLACKNESS) ; BitBlt (hdc, 0, dest_height + dest_yoffset, rect.right, dest_yoffset + 1, NULL, 0, 0, BLACKNESS) ; } } else { dest_xoffset = -renderwin_xoffset ; dest_yoffset = -renderwin_yoffset ; dest_width = GetWidth() ; dest_height = GetHeight() ; GetClientRect (m_Handle, &rect) ; if (rect.right > dest_width) BitBlt (hdc, dest_width, 0, rect.right - dest_width, rect.bottom, NULL, 0, 0, BLACKNESS) ; if (rect.bottom > dest_height) BitBlt (hdc, 0, dest_height, rect.right, rect.bottom - dest_height, NULL, 0, 0, BLACKNESS) ; } if (StretchDIBits (hdc, dest_xoffset, dest_yoffset, dest_width, dest_height, 0, 0, m_Bitmap.header.biWidth, m_Bitmap.header.biHeight, m_BitmapSurface, (LPBITMAPINFO) &m_Bitmap, DIB_RGB_COLORS, SRCCOPY) <= 0) { // hmmmm ... it seems we've run into a Windows bug of some form. When rendering a // large scene file (it used some 200mb of swap plus 80+mb of real memory on a 128mb // box) at a resolution of 1280x1024 (same as screen resolution) on Windows NT 4.0, // StretchDIBits () was observed to return zero (which is not failure, but not success // either :). GetClientRect (m_Handle, &rect) ; BitBlt (hdc, 0, 0, dest_width, dest_height, NULL, 0, 0, WHITENESS) ; if (m_ErrorBitmap == NULL) m_ErrorBitmap = LoadBitmap (hInstance, MAKEINTRESOURCE (BMP_STRETCHDIBITS)) ; hdcMemory = CreateCompatibleDC (hdc) ; oldBmp = (HBITMAP)SelectObject (hdcMemory, m_ErrorBitmap) ; BitBlt (hdc, rect.right / 2 - 157, rect.bottom / 2 - 10, 315, 21, hdcMemory, 0, 0, SRCCOPY) ; SelectObject (hdcMemory, oldBmp) ; DeleteDC (hdcMemory) ; } SetStretchBltMode (hdc, oldMode) ; EndPaint (m_Handle, &ps) ; return (0) ; case WM_DESTROY : m_Handle = NULL ; PVEnableMenuItem (CM_RENDERSHOW, MF_GRAYED) ; PVEnableMenuItem (CM_RENDERCLOSE, MF_GRAYED) ; return (0) ; } return (DefWindowProc (m_Handle, message, wParam, lParam)) ; } LRESULT CALLBACK WinLegacyDisplay::StaticWindowProc (HWND handle, UINT message, WPARAM wParam, LPARAM lParam) { #pragma warning(push) #pragma warning(disable : 4312) WinLegacyDisplay *p = reinterpret_cast (GetWindowLongPtr (handle, 0)) ; #pragma warning(pop) if (p == NULL) { // this is possible during window creation (we haven't yet set the value) return (DefWindowProc (handle, message, wParam, lParam)) ; } if (p->m_Handle == NULL) p->m_Handle = handle ; return p->WindowProc (message, wParam, lParam); } HPALETTE WinLegacyDisplay::CreatePalette (RGBQUAD *rgb, int entries, bool use8bpp) { int i ; HDC hdc ; LogPal Palette = { 0x300, 256 } ; if (use8bpp) { if (rgb) { Palette.entries = entries ; for (i = 0 ; i < entries ; i++, rgb++) { Palette.pe [i].peRed = rgb->rgbRed ; Palette.pe [i].peGreen = rgb->rgbGreen ; Palette.pe [i].peBlue = rgb->rgbBlue ; Palette.pe [i].peFlags = PC_NOCOLLAPSE ; } } else { // Copy the halftone palette into the DIB palette entries, and read the // current system palette entries to ensure we have an identity palette mapping. hdc = GetDC (NULL) ; memcpy (m_BitmapTemplate.colors, halftonePal, sizeof (halftonePal)) ; GetSystemPaletteEntries (hdc, 0, 256, Palette.pe) ; for (i = 0 ; i < 10 ; i++) { m_BitmapTemplate.colors [i].rgbRed = Palette.pe [i].peRed ; m_BitmapTemplate.colors [i].rgbGreen = Palette.pe [i].peGreen ; m_BitmapTemplate.colors [i].rgbBlue = Palette.pe [i].peBlue ; Palette.pe [i].peFlags = 0 ; m_BitmapTemplate.colors [i + 246].rgbRed = Palette.pe [i + 246].peRed ; m_BitmapTemplate.colors [i + 246].rgbGreen = Palette.pe [i + 246].peGreen ; m_BitmapTemplate.colors [i + 246].rgbBlue = Palette.pe [i + 246].peBlue ; Palette.pe [i + 246].peFlags = 0 ; } while (i < 246) { Palette.pe [i].peRed = m_BitmapTemplate.colors [i].rgbRed ; Palette.pe [i].peGreen = m_BitmapTemplate.colors [i].rgbGreen ; Palette.pe [i].peBlue = m_BitmapTemplate.colors [i].rgbBlue ; Palette.pe [i++].peFlags = PC_NOCOLLAPSE ; } ReleaseDC (NULL, hdc) ; } return (::CreatePalette ((LOGPALETTE *) &Palette)) ; } return (NULL) ; } }