Programy napisane w WinaAPI a nowe Windowsy

2

Omówienie różnych sposobów wyszarzania obrazu:

http://www.tannerhelland.com/3643/grayscale-image-algorithm-vb6/

1
Azarien napisał(a):

Jeśli wydajność jest istotna (skoro to mają być bitmapy „hurtowo”) to obróbkę grafiki bym zrobił w OpenGL-u na GPU.

Chyba mi się za bardzo nudziło, bo popełniłem taki program (konsolowy) który wczytuje plik .png, robi szaro za pomocą OpenGL-a i zapisuje do png.

// helper.h

#pragma once

struct bitmap_t
{
	int width, height;
	unsigned char *data;
};

struct bitmap_t load_bitmap_from_file(const wchar_t *fileName);
void save_bitmap_to_file(struct bitmap_t bitmap, const wchar_t *fileName);
struct bitmap_t new_bitmap(int width, int height);
void free_bitmap(struct bitmap_t bitmap);

Jednak napisanie tych funkcji pozostawiamy jako zadanie czytelnikowi ;-)

Właściwy program:

#include <assert.h>
#include <Windows.h>
#include <shlwapi.h>
#include <wincodec.h>
#include <gl/glew.h>

#include "helper.h"

#pragma comment(lib, "opengl32.lib")

void setup_window()
{
	WNDCLASS wc = {
		.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC,
		.lpfnWndProc = DefWindowProc,
		.lpszClassName = L"szary",
		.hInstance = GetModuleHandle(NULL)
	};
	ATOM atom = RegisterClass(&wc);
	HWND hwnd = CreateWindow(
		MAKEINTATOM(atom),
		L"Szary",
		WS_POPUP,
		CW_USEDEFAULT, CW_USEDEFAULT,
		320, 240,
		(HWND)NULL, (HMENU)NULL, wc.hInstance, (LPVOID)NULL
	);
	PIXELFORMATDESCRIPTOR pfd = {
		.nSize = sizeof(pfd),
		.nVersion = 1,
		.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL,
		.cColorBits = 32
	};
	HDC dc = GetDC(hwnd);
	SetPixelFormat(dc, ChoosePixelFormat(dc, &pfd), NULL);
	HGLRC rc = wglCreateContext(dc);
	assert(rc);
	wglMakeCurrent(dc, rc);
	glewInit();
}

void setup_framebuffer(int width, int height)
{
	GLuint renderbuffer;
	glGenRenderbuffersEXT(1, &renderbuffer);
	glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, renderbuffer);
	glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA8, width, height);
	GLuint framebuffer;
	glGenFramebuffersEXT(1, &framebuffer);
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer);
	glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, renderbuffer);
	GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
	assert(status == GL_FRAMEBUFFER_COMPLETE_EXT);
}

const char *fs_source =
"#version 120\n                                                        "
"uniform sampler2D u_tex;                                              "
"void main()                                                           "
"{                                                                     "
"    vec4 texel = texture2D(u_tex, gl_TexCoord[0].st);                 "
"    float gray = texel.r * 0.299 + texel.g * 0.587 + texel.b * 0.114; "
"    gl_FragColor = vec4(gray, gray, gray, texel.a);                   "
"}                                                                     ";

void setup_shader()
{
	GLuint shader = glCreateShader(GL_FRAGMENT_SHADER);
	glShaderSource(shader, 1, &fs_source, NULL);
	glCompileShader(shader);
	GLint status;
	glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
	assert(status);
	GLuint program = glCreateProgram();
	glAttachShader(program, shader);
	glLinkProgram(program);
	glUseProgram(program);
	GLint u_tex = glGetUniformLocation(program, "u_tex");
	glUniform1i(u_tex, 0);
}

void setup_transform(int width, int height)
{
	float matrix[] = { 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, -1, -1, 0, 1 };
	matrix[0] /= width;
	matrix[5] /= height;
	glLoadMatrixf(matrix);
	glViewport(0, 0, width, height);
}

void draw_quad(int width, int height)
{
	glBegin(GL_QUADS);
	glTexCoord2f(0, 0); glVertex2f(0, 0);
	glTexCoord2f(1, 0); glVertex2f(width, 0);
	glTexCoord2f(1, 1); glVertex2f(width, height);
	glTexCoord2f(0, 1); glVertex2f(0, height);
	glEnd();
	glFinish();
}

void setup_texture(struct bitmap_t bitmap)
{
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, bitmap.width, bitmap.height, 0, GL_BGRA, GL_UNSIGNED_BYTE, bitmap.data);
}

int main()
{
	CoInitialize(NULL);

	struct bitmap_t srcBitmap = load_bitmap_from_file(L"image.png");

	setup_window();
	setup_framebuffer(srcBitmap.width, srcBitmap.height);
	setup_shader();
	setup_transform(srcBitmap.width, srcBitmap.height);
	setup_texture(srcBitmap);
	draw_quad(srcBitmap.width, srcBitmap.height);

	struct bitmap_t dstBitmap = new_bitmap(srcBitmap.width, srcBitmap.height);
	glReadPixels(0, 0, dstBitmap.width, dstBitmap.height, GL_BGRA, GL_UNSIGNED_BYTE, dstBitmap.data);

	save_bitmap_to_file(dstBitmap, L"image_gray.png");
	
	free_bitmap(srcBitmap);
	free_bitmap(dstBitmap);

	CoUninitialize();
	return 0;
}

Program napisany pod Visualem 2017. Z zewnętrznych bibliotek używa jedynie GLEW (paczka z NuGeta unofficial-flayan-glew) ale dałoby się jej uniknąć.

Laptop na którym to pisałem ma OpenGL 2.1, więc pod taką wersję kod jest pisany.
W niektórych miejscach poszedłem na łatwiznę. Reklamacje że to czy tamto jest „deprecated” nie będą uwzględniane ;-)

Program nie otwiera żadnego (widocznego) okienka. Funkcja setup_window() tworzy okno bo musi, ale go nie pokazuje.

1 użytkowników online, w tym zalogowanych: 0, gości: 1