/* * EEZ Modular Firmware * Copyright (C) 2015-present, Envox d.o.o. * * 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 . */ #include #include #include #include #include #if !defined(__EMSCRIPTEN__) #include #include #endif #include #include #include #include #include #include #include #include #if OPTION_MOUSE #include #endif #include namespace eez { namespace gui { namespace display { //////////////////////////////////////////////////////////////////////////////// #if !defined(__EMSCRIPTEN__) static SDL_Window *g_mainWindow; static SDL_Renderer *g_renderer; #endif //////////////////////////////////////////////////////////////////////////////// // heuristics to find resource file std::string getFullPath(std::string category, std::string path) { std::string fullPath = category + "/" + path; for (int i = 0; i < 5; ++i) { FILE *fp = fopen(fullPath.c_str(), "r"); if (fp) { fclose(fp); return fullPath; } fullPath = std::string("../") + fullPath; } return path; } void initDriver() { #if !defined(__EMSCRIPTEN__) // Set texture filtering to linear if (!SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1")) { printf("Warning: Linear texture filtering not enabled!"); } // Create window g_mainWindow = SDL_CreateWindow(TITLE, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, DISPLAY_WIDTH, DISPLAY_HEIGHT, SDL_WINDOW_HIDDEN); if (g_mainWindow == NULL) { printf("Window could not be created! SDL Error: %s\n", SDL_GetError()); return; } // Create renderer g_renderer = SDL_CreateRenderer(g_mainWindow, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); if (g_renderer == NULL) { g_mainWindow = NULL; printf("Renderer could not be created! SDL Error: %s\n", SDL_GetError()); return; } SDL_SetRenderDrawBlendMode(g_renderer, SDL_BLENDMODE_BLEND); // Initialize PNG loading int imgFlags = IMG_INIT_PNG; if ((IMG_Init(imgFlags) & imgFlags) != imgFlags) { printf("SDL_image could not initialize! SDL_image Error: %s\n", IMG_GetError()); } else { #if !defined(__EMSCRIPTEN__) // Set icon SDL_Surface *iconSurface = IMG_Load(getFullPath("images", ICON).c_str()); if (!iconSurface) { printf("Failed to load icon! SDL Error: %s\n", SDL_GetError()); } else { SDL_SetWindowIcon(g_mainWindow, iconSurface); SDL_FreeSurface(iconSurface); } #endif } SDL_ShowWindow(g_mainWindow); #if OPTION_MOUSE if (mouse::isMouseEnabled()) { SDL_ShowCursor(SDL_DISABLE); SDL_CaptureMouse(SDL_TRUE); } #endif #endif } void syncBuffer() { #if !defined(__EMSCRIPTEN__) if (!g_mainWindow) { return; } SDL_Surface *rgbSurface = SDL_CreateRGBSurfaceFrom( (uint32_t *)g_syncedBuffer, DISPLAY_WIDTH, DISPLAY_HEIGHT, 32, 4 * DISPLAY_WIDTH, 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000); if (rgbSurface != NULL) { SDL_Texture *texture = SDL_CreateTextureFromSurface(g_renderer, rgbSurface); if (texture != NULL) { SDL_Rect srcRect = { 0, 0, (int)DISPLAY_WIDTH, (int)DISPLAY_HEIGHT }; SDL_Rect dstRect = { 0, 0, (int)DISPLAY_WIDTH, (int)DISPLAY_HEIGHT }; SDL_RenderCopyEx(g_renderer, texture, &srcRect, &dstRect, 0.0, NULL, SDL_FLIP_NONE); SDL_DestroyTexture(texture); } else { printf("Unable to create texture from image buffer! SDL Error: %s\n", SDL_GetError()); } SDL_FreeSurface(rgbSurface); } else { printf("Unable to render text surface! SDL Error: %s\n", SDL_GetError()); } SDL_RenderPresent(g_renderer); sendMessageToGuiThread(GUI_QUEUE_MESSAGE_TYPE_DISPLAY_VSYNC, 0, 0); #endif touch::tick(); } void copySyncedBufferToScreenshotBuffer() { auto appContext = getAppContextFromId(APP_CONTEXT_ID_DEVICE); uint8_t *src = (uint8_t *)(g_renderBuffer + appContext->rect.y * DISPLAY_WIDTH + appContext->rect.x); uint8_t *dst = SCREENSHOOT_BUFFER_START_ADDRESS; int srcAdvance = (DISPLAY_WIDTH - 480) * 4; for (int y = 0; y < 272; y++) { for (int x = 0; x < 480; x++) { uint8_t r = *src++; uint8_t g = *src++; uint8_t b = *src++; src++; *dst++ = r; *dst++ = g; *dst++ = b; } src += srcAdvance; } } //////////////////////////////////////////////////////////////////////////////// void startPixelsDraw() { } void drawPixel(int x, int y) { *(g_renderBuffer + y * DISPLAY_WIDTH + x) = color16to32(g_fc); } void drawPixel(int x, int y, uint8_t opacity) { auto dest = g_renderBuffer + y * DISPLAY_WIDTH + x; auto destUint8 = (uint8_t *)dest; *dest = blendColor( color16to32(g_fc, opacity), color16to32(RGB_TO_COLOR(destUint8[0], destUint8[1], destUint8[2]), 255 - opacity)); } void endPixelsDraw() { setDirty(); } void fillRect(int x1, int y1, int x2, int y2) { uint32_t color32 = color16to32(g_fc, g_opacity); uint32_t *dst = g_renderBuffer + y1 * DISPLAY_WIDTH + x1; int width = x2 - x1 + 1; int height = y2 - y1 + 1; int nl = DISPLAY_WIDTH - width; if (g_opacity == 255) { for (uint32_t *dstEnd = dst + height * DISPLAY_WIDTH; dst != dstEnd; dst += nl) { for (uint32_t *lineEnd = dst + width; dst != lineEnd; dst++) { *dst = color32; } } } else { for (uint32_t *dstEnd = dst + height * DISPLAY_WIDTH; dst != dstEnd; dst += nl) { for (uint32_t *lineEnd = dst + width; dst != lineEnd; dst++) { *dst = blendColor(color32, *dst); } } } setDirty(); } void fillRect(void *dstBuffer, int x1, int y1, int x2, int y2) { uint32_t color32 = color16to32(g_fc); uint32_t *dst = (uint32_t *)dstBuffer + y1 * DISPLAY_WIDTH + x1; int nl = DISPLAY_WIDTH - (x2 - x1 + 1); for (int y = y1; y <= y2; y++) { for (int x = x1; x <= x2; x++) { *dst++ = color32; } dst += nl; } setDirty(); } void bitBlt(int x1, int y1, int x2, int y2, int dstx, int dsty) { int width = x2 - x1 + 1; uint32_t *src = g_renderBuffer + y1 * DISPLAY_WIDTH + x1; uint32_t *dst = g_renderBuffer + dsty * DISPLAY_WIDTH + dstx; int nl = DISPLAY_WIDTH - width; for (int y = y1; y <= y2; y++, src += nl, dst += nl) { for (uint32_t *lineEnd = dst + width; dst != lineEnd; dst++, src++) { uint8_t *src8 = (uint8_t *)src; *dst = color16to32(RGB_TO_COLOR(src8[0], src8[1], src8[2])); } } setDirty(); } void bitBlt(void *src, int x1, int y1, int x2, int y2) { bitBlt(src, g_renderBuffer, x1, y1, x2, y2); setDirty(); } void bitBlt(void *src, void *dst, int x1, int y1, int x2, int y2) { for (int y = y1; y <= y2; ++y) { for (int x = x1; x <= x2; ++x) { int i = y * DISPLAY_WIDTH + x; ((uint32_t *)dst)[i] = ((uint32_t *)src)[i]; } } setDirty(); } void bitBlt(void *src, void *dst, int sx, int sy, int sw, int sh, int dx, int dy, uint8_t opacity) { if (dst == nullptr) { dst = g_renderBuffer; } if (opacity == 255) { for (int y = 0; y < sh; ++y) { for (int x = 0; x < sw; ++x) { ((uint32_t *)dst)[(dy + y) * DISPLAY_WIDTH + dx + x] = ((uint32_t *)src)[(sy + y) * DISPLAY_WIDTH + sx + x]; } } } else { for (int y = 0; y < sh; ++y) { for (int x = 0; x < sw; ++x) { uint8_t *p = (uint8_t *)&((uint32_t *)src)[(sy + y) * DISPLAY_WIDTH + sx + x]; p[3] = opacity; ((uint32_t *)dst)[(dy + y) * DISPLAY_WIDTH + dx + x] = blendColor( ((uint32_t *)src)[(sy + y) * DISPLAY_WIDTH + sx + x], ((uint32_t *)dst)[(dy + y) * DISPLAY_WIDTH + dx + x] ); } } } } void drawBitmap(Image *image, int x, int y) { uint32_t *dst = g_renderBuffer + y * DISPLAY_WIDTH + x; int nlDst = DISPLAY_WIDTH - image->width; if (image->bpp == 32) { uint32_t *src = (uint32_t *)image->pixels; int nlSrc = image->lineOffset; uint32_t pixel; uint8_t *pixelAlpha = ((uint8_t *)&pixel) + 3; for (uint32_t *srcEnd = src + (image->width + nlSrc) * image->height; src != srcEnd; src += nlSrc, dst += nlDst) { for (uint32_t *lineEnd = dst + image->width; dst != lineEnd; src++, dst++) { pixel = *src; *pixelAlpha = *pixelAlpha * g_opacity / 255; *dst = blendColor(pixel, *dst); } } } else if (image->bpp == 24) { uint8_t *src = (uint8_t *)image->pixels; int nlSrc = 3 * image->lineOffset; for (uint8_t *srcEnd = src + 3 * (image->width + nlSrc) * image->height; src != srcEnd; src += nlSrc, dst += nlDst) { for (uint32_t *lineEnd = dst + image->width; dst != lineEnd; src += 3, dst++) { ((uint8_t *)dst)[0] = ((uint8_t *)src)[0]; ((uint8_t *)dst)[1] = ((uint8_t *)src)[1]; ((uint8_t *)dst)[2] = ((uint8_t *)src)[2]; ((uint8_t *)dst)[3] = 255; } } } else { uint16_t *src = (uint16_t *)image->pixels; int nlSrc = image->lineOffset; for (uint16_t *srcEnd = src + (image->width + nlSrc) * image->height; src != srcEnd; src += nlSrc, dst += nlDst) { for (uint32_t *lineEnd = dst + image->width; dst != lineEnd; src++, dst++) { *dst = color16to32(*src); } } } setDirty(); } void drawStrInit() { } void drawGlyph(const uint8_t *src, uint32_t srcLineOffset, int x_glyph, int y_glyph, int width, int height) { // glyph->pixels + offset + iStartByte, glyph->width - width, x_glyph, y_glyph, width,height // const gui::GlyphData &glyph, int x_glyph, int y_glyph, int width, int height, int offset, int iStartByte uint32_t pixel; ((uint8_t *)&pixel)[0] = COLOR_TO_R(g_fc); ((uint8_t *)&pixel)[1] = COLOR_TO_G(g_fc); ((uint8_t *)&pixel)[2] = COLOR_TO_B(g_fc); uint8_t *pixelAlpha = ((uint8_t *)&pixel) + 3; uint32_t *dst = g_renderBuffer + y_glyph * DISPLAY_WIDTH + x_glyph; int nlDst = DISPLAY_WIDTH - width; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { *pixelAlpha = *src * g_opacity / 255; *dst = blendColor(pixel, *dst); src++; dst++; } src += srcLineOffset; dst += nlDst; } } } // namespace display } // namespace gui } // namespace eez #if defined(__EMSCRIPTEN__) EM_PORT_API(uint8_t*) getSyncedBuffer() { using namespace eez::gui; sendMessageToGuiThread(GUI_QUEUE_MESSAGE_TYPE_DISPLAY_VSYNC, 0, 0); return (uint8_t*)display::g_syncedBuffer; } #endif