Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create a uiImage control #209

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions common/controlsigs.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#define uiFormSignature 0x466F726D
#define uiGridSignature 0x47726964
#define uiGroupSignature 0x47727062
#define uiImageSignature 0x4723BDC6
#define uiLabelSignature 0x4C61626C
#define uiMultilineEntrySignature 0x4D6C6E45
#define uiProgressBarSignature 0x50426172
Expand Down
1 change: 1 addition & 0 deletions darwin/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ list(APPEND _LIBUI_SOURCES
darwin/form.m
darwin/grid.m
darwin/group.m
darwin/image.m
darwin/label.m
darwin/main.m
darwin/map.m
Expand Down
37 changes: 37 additions & 0 deletions darwin/image.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// 14 september 2016
#import "uipriv_darwin.h"

struct uiImage {
uiDarwinControl c;
NSImage *image;
NSImageView *imageView;
};

uiDarwinControlAllDefaults(uiImage, imageView)

void uiImageSetSize(uiImage *i, unsigned int width, unsigned int height)
{
i->image.size = NSMakeSize(width, height);
[i->imageView setNeedsDisplay:YES];
}

void uiImageGetSize(uiImage *i, unsigned int *width, unsigned int *height)
{
NSSize size = i->image.size;
*width = size.width;
*height = size.height;
}

uiImage *uiNewImage(const char *filename)
{
uiImage *i;

uiDarwinNewControl(uiImage, i);

i->image = [[NSImage alloc] initWithContentsOfFile:[NSString stringWithUTF8String:filename]];

i->imageView = [[NSImageView alloc] init];
[i->imageView setImage:i->image];

return i;
}
6 changes: 6 additions & 0 deletions ui.h
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,12 @@ _UI_EXTERN uiEntry *uiNewEntry(void);
_UI_EXTERN uiEntry *uiNewPasswordEntry(void);
_UI_EXTERN uiEntry *uiNewSearchEntry(void);

typedef struct uiImage uiImage;
#define uiImage(this) ((uiImage *) (this))
_UI_EXTERN void uiImageSetSize(uiImage *i, unsigned int width, unsigned int height);
_UI_EXTERN void uiImageGetSize(uiImage *i, unsigned int *width, unsigned int *height);
_UI_EXTERN uiImage *uiNewImage(const char *filename);

typedef struct uiLabel uiLabel;
#define uiLabel(this) ((uiLabel *) (this))
_UI_EXTERN char *uiLabelText(uiLabel *l);
Expand Down
1 change: 1 addition & 0 deletions unix/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ list(APPEND _LIBUI_SOURCES
unix/graphemes.c
unix/grid.c
unix/group.c
unix/image.c
unix/label.c
unix/main.c
unix/menu.c
Expand Down
42 changes: 42 additions & 0 deletions unix/image.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// 13 september 2016
#include "uipriv_unix.h"

struct uiImage {
uiUnixControl c;
GtkWidget *widget;
};

uiUnixControlAllDefaults(uiImage)

void uiImageSetSize(uiImage *i, unsigned int width, unsigned int height)
{
GdkPixbuf *pixbuf;

pixbuf = gtk_image_get_pixbuf(GTK_IMAGE(i->widget));
pixbuf = gdk_pixbuf_scale_simple(pixbuf,
width,
height,
GDK_INTERP_BILINEAR);
gtk_image_set_from_pixbuf(GTK_IMAGE(i->widget), pixbuf);
g_object_unref(pixbuf);
}

void uiImageGetSize(uiImage *i, unsigned int *width, unsigned int *height)
{
GdkPixbuf *pixbuf;

pixbuf = gtk_image_get_pixbuf(GTK_IMAGE(i->widget));
*width = gdk_pixbuf_get_width(pixbuf);
*height = gdk_pixbuf_get_height(pixbuf);
}

uiImage *uiNewImage(const char *filename)
{
uiImage *img;

uiUnixNewControl(uiImage, img);

img->widget = gtk_image_new_from_file(filename);

return img;
}
3 changes: 2 additions & 1 deletion windows/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ list(APPEND _LIBUI_SOURCES
windows/graphemes.cpp
windows/grid.cpp
windows/group.cpp
windows/image.cpp
windows/init.cpp
windows/label.cpp
windows/main.cpp
Expand Down Expand Up @@ -80,7 +81,7 @@ endmacro()
# notice that usp10 comes before gdi32
# TODO prune this list
set(_LIBUI_LIBS
user32 kernel32 usp10 gdi32 comctl32 uxtheme msimg32 comdlg32 d2d1 dwrite ole32 oleaut32 oleacc uuid
user32 kernel32 usp10 gdi32 comctl32 uxtheme msimg32 comdlg32 d2d1 dwrite ole32 oleaut32 oleacc uuid windowscodecs
PARENT_SCOPE)

if(NOT MSVC)
Expand Down
219 changes: 219 additions & 0 deletions windows/image.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
// 13 september 2016
#include "uipriv_windows.hpp"

struct uiImage {
uiWindowsControl c;
HWND hwnd;
WCHAR *wfilename;
HBITMAP bmpSource;
HDC hdcSource;
LONG width;
LONG height;
LONG displayWidth;
LONG displayHeight;
BOOL originalSize;
};

static HBITMAP ConvertToHBitmap(IWICBitmapSource * ipBitmap)
{
HBITMAP hbmp = NULL;
void * pvImageBits = NULL;
HDC hdcScreen = GetDC(NULL);

UINT width = 0;
UINT height = 0;
if (FAILED(ipBitmap->GetSize(&width, &height)) || width == 0 || height == 0) {
return NULL;
}

BITMAPINFO bminfo = {0};
bminfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bminfo.bmiHeader.biWidth = width;
bminfo.bmiHeader.biHeight = -((LONG) height);
bminfo.bmiHeader.biPlanes = 1;
bminfo.bmiHeader.biBitCount = 32;
bminfo.bmiHeader.biCompression = BI_RGB;

hbmp = CreateDIBSection(hdcScreen, &bminfo, DIB_RGB_COLORS, &pvImageBits, NULL, 0);
ReleaseDC(NULL, hdcScreen);
if (hbmp == NULL) {
return NULL;
}

UINT stride = width * 4;
if (FAILED(ipBitmap->CopyPixels(NULL, stride, stride * height, static_cast<BYTE *>(pvImageBits)))) {
DeleteObject(hbmp);
hbmp = NULL;
}

return hbmp;
}

static HBITMAP LoadImageFromFile(const WCHAR *filename)
{
IWICImagingFactory *wicFactory = NULL;
IWICBitmapDecoder *decoder = NULL;
IWICBitmapFrameDecode *frame = NULL;
IWICBitmapSource *bitmap = NULL;
HRESULT hr;

hr = CoCreateInstance(
CLSID_WICImagingFactory,
NULL,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&wicFactory)
);

if (FAILED(hr)) {
return NULL;
}

hr = wicFactory->CreateDecoderFromFilename(
filename,
NULL,
GENERIC_READ,
WICDecodeMetadataCacheOnDemand,
&decoder
);

if (SUCCEEDED(hr)) {
hr = decoder->GetFrame(0, &frame);
}

WICConvertBitmapSource(GUID_WICPixelFormat32bppPBGRA, frame, &bitmap);
frame->Release();
decoder->Release();

return ConvertToHBitmap(bitmap);
}

static LRESULT CALLBACK imageWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
uiImage *i;
CREATESTRUCTW *cs = (CREATESTRUCTW *) lParam;
PAINTSTRUCT ps;
HDC hdcDestination;
BITMAP bm = {0};

i = (uiImage *) GetWindowLongPtrW(hwnd, GWLP_USERDATA);
if (i == NULL) {
if (uMsg == WM_CREATE) {
i = (uiImage *) (cs->lpCreateParams);
// assign i->hwnd here so we can use it immediately
i->hwnd = hwnd;
SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONG_PTR) i);

// Load the image through the windows imaging component
i->bmpSource = LoadImageFromFile(i->wfilename);

if(i->bmpSource == NULL) {
logLastError(L"Failed to load the image");
}
GetObject(i->bmpSource, sizeof(bm), &bm);
i->width = bm.bmWidth;
i->height = bm.bmHeight;
i->displayWidth = i->width;
i->displayHeight = i->height;
i->hdcSource = CreateCompatibleDC(GetDC(0));
SelectObject(i->hdcSource, i->bmpSource);
return 0;
}
// fall through to DefWindowProcW() anyway
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}

if (uMsg == WM_PAINT) {
hdcDestination = BeginPaint(hwnd, &ps);
if (i->originalSize) {
BitBlt(hdcDestination, 0, 0, i->width, i->height, i->hdcSource, 0, 0, SRCCOPY);
} else {
SetStretchBltMode(hdcDestination,HALFTONE);
StretchBlt(hdcDestination,
0, 0,
i->displayWidth, i->displayHeight,
i->hdcSource,
0, 0,
i->width, i->height,
SRCCOPY);
}
EndPaint(hwnd, &ps);
return 0;
}

// nothing done
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

static void uiImageDestroy(uiControl *c)
{
uiImage *i = uiImage(c);

uiFree(i->wfilename);
uiWindowsEnsureDestroyWindow(i->hwnd);
uiFreeControl(uiControl(i));
}

uiWindowsControlAllDefaultsExceptDestroy(uiImage)

static void uiImageMinimumSize(uiWindowsControl *c, int *width, int *height)
{
uiImage *i = uiImage(c);
*width = i->displayWidth;
*height = i->displayHeight;
}

void uiImageSetSize(uiImage *i, unsigned int width, unsigned int height)
{
i->displayWidth = width;
i->displayHeight = height;
i->originalSize = width == i->width && height == i->height;

invalidateRect(i->hwnd, NULL, FALSE);

// If the image is smaller in either direction the part of the window that
// used to be covered by the image will not get redrawn because it is no
// longer part of the image. To fix this just redraw the entire parent
// window. This feels wrong though.
invalidateRect(GetParent(i->hwnd), NULL, FALSE);
}

void uiImageGetSize(uiImage *i, unsigned int *width, unsigned int *height)
{
*width = i->displayWidth;
*height = i->displayHeight;
}

void unregisterImage(void)
{
if (UnregisterClassW(imageClass, hInstance) == 0)
logLastError(L"error unregistering uiImage window class");
}

ATOM registerImageClass(HICON hDefaultIcon, HCURSOR hDefaultCursor)
{
WNDCLASSW wc;

ZeroMemory(&wc, sizeof (WNDCLASSW));
wc.lpszClassName = imageClass;
wc.lpfnWndProc = imageWndProc;
wc.hInstance = hInstance;
wc.hIcon = hDefaultIcon;
wc.hCursor = hDefaultCursor;
return RegisterClassW(&wc);
}

uiImage *uiNewImage(const char *filename)
{
uiImage *image;

uiWindowsNewControl(uiImage, image);
image->wfilename = toUTF16(filename);

uiWindowsEnsureCreateControlHWND(0,
imageClass, L"",
0,
hInstance, image,
FALSE);

return image;
}
3 changes: 3 additions & 0 deletions windows/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ const char *uiInit(uiInitOptions *o)
if (registerAreaClass(hDefaultIcon, hDefaultCursor) == 0)
return ieLastErr("registering uiArea window class");

if (registerImageClass(hDefaultIcon, hDefaultCursor) == 0)
return ieLastErr("registering uiImage window class");

if (registerMessageFilter() == 0)
return ieLastErr("registering libui message filter");

Expand Down
5 changes: 5 additions & 0 deletions windows/uipriv_windows.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,11 @@ extern HWND newD2DScratch(HWND parent, RECT *rect, HMENU controlID, SUBCLASSPROC
extern ATOM registerAreaClass(HICON, HCURSOR);
extern void unregisterArea(void);

// image.cpp
#define imageClass L"libui_uiImageClass"
extern ATOM registerImageClass(HICON, HCURSOR);
extern void unregisterImage(void);

// areaevents.cpp
extern BOOL areaFilter(MSG *);

Expand Down
1 change: 1 addition & 0 deletions windows/winapi.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include <d2d1helper.h>
#include <dwrite.h>
#include <usp10.h>
#include <wincodec.h>

#include <stdint.h>
#include <string.h>
Expand Down