/************************************************************************ MeOS - Orienteering Software Copyright (C) 2009-2022 Melin Software HB This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Melin Software HB - software@melin.nu - www.melin.nu Eksoppsvägen 16, SE-75646 UPPSALA, Sweden ************************************************************************/ #include "stdafx.h" #include "image.h" #include "png/png.h" #include #include #include #include #include "meosexception.h" FILE _iob[] = { *stdin, *stdout, *stderr }; extern "C" FILE * __cdecl __iob_func(void) { return _iob; } namespace { struct PngData { vector memory; size_t ptr; PngData() : ptr(0) {} size_t read(uint8_t *dst, size_t count); }; void readDataFromInputStream(png_structp png_ptr, png_bytep outBytes, png_size_t byteCountToRead) { png_voidp io_ptr = png_get_io_ptr(png_ptr); if (io_ptr == NULL) return; // add custom error handling here PngData& inputStream = *(PngData*)io_ptr; const size_t bytesRead = inputStream.read((byte*)outBytes, (size_t)byteCountToRead); if ((png_size_t)bytesRead != byteCountToRead) return; // add custom error handling here } } size_t PngData::read(uint8_t *dst, size_t count) { count = min(size_t(memory.size() - ptr), count); memcpy(dst, &memory[ptr], count); ptr += count; return count; } // Creates a stream object initialized with the data from an executable resource. vector Image::loadResourceToMemory(LPCTSTR lpName, LPCTSTR lpType) { vector result; // find the resource HRSRC hrsrc = FindResource(NULL, lpName, lpType); if (hrsrc == NULL) return result; // load the resource DWORD dwResourceSize = SizeofResource(NULL, hrsrc); HGLOBAL hglbImage = LoadResource(NULL, hrsrc); if (hglbImage == NULL) return result; // lock the resource, getting a pointer to its data LPVOID pvSourceResourceData = LockResource(hglbImage); result.resize(dwResourceSize); memcpy(&result[0], pvSourceResourceData, dwResourceSize); return result; } HBITMAP Image::read_png(vector &inData, int &width, int &height, ImageMethod method) { PngData inputStream; inputStream.memory.swap(inData); png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png) return nullptr; png_infop info = png_create_info_struct(png); if (!info) return nullptr; png_set_read_fn(png, &inputStream, readDataFromInputStream); png_read_info(png, info); width = png_get_image_width(png, info); height = png_get_image_height(png, info); int color_type = png_get_color_type(png, info); int bit_depth = png_get_bit_depth(png, info); // Read any color_type into 8bit depth, RGBA format. // See http://www.libpng.org/pub/png/libpng-manual.txt if (bit_depth == 16) png_set_strip_16(png); if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb(png); // PNG_COLOR_TYPE_GRAY_ALPHA is always 8 or 16bit depth. if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) png_set_expand_gray_1_2_4_to_8(png); if (png_get_valid(png, info, PNG_INFO_tRNS)) png_set_tRNS_to_alpha(png); // These color_type don't have an alpha channel then fill it with 0xff. if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_PALETTE) png_set_filler(png, 0xFF, PNG_FILLER_AFTER); if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(png); png_read_update_info(png, info); int rowb = png_get_rowbytes(png, info); vector> data(height, vector(rowb, 0)); vector row_pointers_vec(height); for (int y = 0; y < height; y++) { row_pointers_vec[y] = &data[y][0]; } png_bytepp row_pointers = &row_pointers_vec[0]; png_read_image(png, row_pointers); // initialize return value HBITMAP hbmp = NULL; // prepare structure giving bitmap information (negative height indicates a top-down DIB) BITMAPINFO bminfo; ZeroMemory(&bminfo, sizeof(bminfo)); 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; // create a DIB section that can hold the image void * pvImageBits = NULL; HDC hdcScreen = GetDC(NULL); hbmp = CreateDIBSection(hdcScreen, &bminfo, DIB_RGB_COLORS, &pvImageBits, NULL, 0); ReleaseDC(NULL, hdcScreen); // extract the image into the HBITMAP const size_t cbStride = width * 4; const size_t cbImage = cbStride * height; byte *dst = static_cast(pvImageBits); for (int y = 0; y < height; y++) { byte *row = dst + cbStride * y; byte *src = row_pointers_vec[y]; if (method == ImageMethod::MonoAlpha) { for (size_t x = 0; x < cbStride; x += 4) { row[x + 2] = 0;// src[x + 0]; // Red row[x + 1] = 0;// src[x + 1]; // Green row[x + 0] = 16;// src[x + 2]; // Blue row[x + 3] = 255 - src[x + 0];// ((x/100)%8)*31+1;// 255 - src[x + 0]; // Alpha if (row[x + 3] == 0) { row[x + 1] = 0; row[x + 2] = 0; row[x + 0] = 0; } } } else if (method == ImageMethod::Default) { for (size_t x = 0; x < cbStride; x += 4) { row[x + 2] = src[x + 0]; // Red row[x + 1] = src[x + 1]; // Green row[x + 0] = src[x + 2]; // Blue row[x + 3] = src[x + 3]; } } } return hbmp; } HBITMAP Image::read_png_file(const wstring &filename, int &width, int &height, ImageMethod method) { width = 0; height = 0; PngData inputStream; inputStream.memory; ifstream fin; fin.open(filename, ios::binary); fin.seekg(0, ios::end); int p2 = (int)fin.tellg(); fin.seekg(0, ios::beg); inputStream.memory.resize(p2); fin.read((char *)&inputStream.memory[0], inputStream.memory.size()); fin.close(); return read_png(inputStream.memory, width, height, method); } HBITMAP Image::read_png_resource(LPCTSTR lpName, LPCTSTR lpType, int &width, int &height, ImageMethod method) { width = 0; height = 0; PngData inputStream; inputStream.memory = loadResourceToMemory(lpName, lpType); if (inputStream.memory.empty()) return nullptr; return read_png(inputStream.memory, width, height, method); } Image::Image() { } Image::~Image() { } // Loads the PNG containing the splash image into a HBITMAP. HBITMAP Image::loadImage(int resource, ImageMethod method) { if (images.count(resource)) return images[resource].image; int width, height; HBITMAP hbmp = read_png_resource(MAKEINTRESOURCE(resource), _T("PNG"), width, height, method); if (hbmp != 0) { images[resource].image = hbmp; images[resource].width = width; images[resource].height = height; } return hbmp; } int Image::getWidth(int resource) { loadImage(resource, ImageMethod::Default); return images[resource].width; } int Image::getHeight(int resource) { loadImage(resource, ImageMethod::Default); return images[resource].height; } void Image::drawImage(int resource, ImageMethod method, HDC hDC, int x, int y, int width, int height) { HBITMAP bmp = loadImage(resource, method); HDC memdc = CreateCompatibleDC(hDC); SelectObject(memdc, bmp); BLENDFUNCTION bf; bf.BlendOp = AC_SRC_OVER; bf.BlendFlags = 0; bf.SourceConstantAlpha =0xFF; bf.AlphaFormat = AC_SRC_ALPHA; AlphaBlend(hDC, x, y, width, height, memdc, 0, 0, width, height, bf); DeleteDC(memdc); }