/* * Apple // emulator for *ix * * This software package is subject to the GNU General Public License * version 3 or later (your choice) as published by the Free Software * Foundation. * * Copyright 2013-2015 Aaron Culliney * */ #include "glhudmodel.h" #include "glnode.h" // . . . // . x . // . . . typedef struct EightPatchArgs_s { GLModel *parent; unsigned int pixelSize; unsigned int glyphScale; unsigned int fb_h; unsigned int fb_w; unsigned int srcIdx; unsigned int dstIdx; } EightPatchArgs_s; #ifndef NDEBUG # define SET_TEX_PIXEL(OFF) \ do { \ assert((OFF) >= 0 && (OFF) < lastPoint); \ *((PIXEL_TYPE*)(texPixels + (OFF))) |= SEMI_OPAQUE; \ } while (0); #else # define SET_TEX_PIXEL(OFF) \ do { \ *((PIXEL_TYPE*)(texPixels + (OFF))) |= SEMI_OPAQUE; \ } while (0); #endif interface_colorscheme_t glhud_currentColorScheme = RED_ON_BLACK; // Generates a semi-opaque halo effect around each glyph static void _eightpatch_opaquePixelHaloFilter(const EightPatchArgs_s args) { #if USE_RGBA4444 # define SEMI_OPAQUE (0x0C << SHIFT_A) #else # define SEMI_OPAQUE (0xC0 << SHIFT_A) #endif const unsigned int pixelSize = args.pixelSize; const unsigned int glyphScale = args.glyphScale; const unsigned int fb_w = args.fb_w; const unsigned int fb_h = args.fb_h; const unsigned int srcIdx = args.srcIdx; const unsigned int srcCol = (srcIdx % fb_w); const unsigned int lastCol = (fb_w-1); const unsigned int dstPointStride = pixelSize * glyphScale; const unsigned int dstRowStride = fb_w * dstPointStride; const unsigned int texRowStride = ((fb_w * glyphScale) * (FONT_GLYPH_SCALE_Y * glyphScale) * pixelSize); const unsigned int lastPoint = ((fb_w * glyphScale) * (fb_h * glyphScale) * pixelSize); uint8_t *texPixels = args.parent->texPixels; const int dstIdx0 = (int)args.dstIdx; const int dstPre0 = dstIdx0 - texRowStride; // negative is okay const int dstPost0 = dstIdx0 + texRowStride; // scale glyph data 1x, 2x, ... // north pixels if (dstPre0 >= 0) { int dstPre = dstPre0; for (unsigned int k=0; kglyphMultiplier = 1; return hudElement; } void glhud_setupDefault(GLModel *parent) { GLModelHUDElement *hudElement = (GLModelHUDElement *)parent->custom; assert(hudElement->glyphMultiplier > 0); hudElement->colorScheme = glhud_currentColorScheme; char *submenu = (char *)(hudElement->tpl); const unsigned int cols = hudElement->tplWidth; const unsigned int rows = hudElement->tplHeight; PIXEL_TYPE *fb = hudElement->pixels; // render template into indexed fb interface_plotMessage(fb, hudElement->colorScheme, submenu, cols, rows); // generate OpenGL texture/color from indexed color const unsigned int fb_w = hudElement->pixWidth; const unsigned int fb_h = hudElement->pixHeight; const unsigned int pixelSize = sizeof(PIXEL_TYPE); const unsigned int glyphScale = hudElement->glyphMultiplier; const unsigned int dstPointStride = pixelSize * glyphScale; const unsigned int dstRowStride = fb_w * dstPointStride; const unsigned int texSubRowStride = dstRowStride * (glyphScale-1); do { unsigned int srcIdx = 0; unsigned int texIdx = 0; #ifndef NDEBUG const unsigned int srcLastPoint = fb_w * fb_h; const unsigned int texLastPoint = ((fb_w * glyphScale) * (fb_h * glyphScale) * pixelSize); #endif for (unsigned int i=0; iblackIsTransparent) { // black remains transparent } else { rgba |= ((PIXEL_TYPE)MAX_SATURATION << SHIFT_A); } // scale glyph data 1x, 2x, ... unsigned int dstIdx = texIdx; for (unsigned int k=0; ktexPixels + dstIdx) ) = rgba; } dstIdx -= dstPointStride; } } } #ifndef NDEBUG assert(srcIdx == srcLastPoint); assert(texIdx == texLastPoint); #endif } while (0); if (hudElement->opaquePixelHalo) { unsigned int srcIdx = 0; unsigned int texIdx = 0; #ifndef NDEBUG const unsigned int srcLastPoint = fb_w * fb_h; const unsigned int texLastPoint = ((fb_w * glyphScale) * (fb_h * glyphScale) * pixelSize); #endif for (unsigned int i=0; itexDirty = true; } void glhud_renderDefault(GLModel *parent) { // Bind our vertex array object #if USE_VAO glBindVertexArray(parent->vaoName); #else glBindBuffer(GL_ARRAY_BUFFER, parent->posBufferName); GLsizei posTypeSize = getGLTypeSize(parent->positionType); GLsizei texcoordTypeSize = getGLTypeSize(parent->texcoordType); // Set up parmeters for position attribute in the VAO including, size, type, stride, and offset in the currenly // bound VAO This also attaches the position VBO to the VAO glVertexAttribPointer(POS_ATTRIB_IDX, // What attibute index will this array feed in the vertex shader (see buildProgram) parent->positionSize, // How many elements are there per position? parent->positionType, // What is the type of this data? GL_FALSE, // Do we want to normalize this data (0-1 range for fixed-pont types) parent->positionSize*posTypeSize, // What is the stride (i.e. bytes between positions)? 0); // What is the offset in the VBO to the position data? glEnableVertexAttribArray(POS_ATTRIB_IDX); // Set up parmeters for texcoord attribute in the VAO including, size, type, stride, and offset in the currenly // bound VAO This also attaches the texcoord VBO to VAO glBindBuffer(GL_ARRAY_BUFFER, parent->texcoordBufferName); glVertexAttribPointer(TEXCOORD_ATTRIB_IDX, // What attibute index will this array feed in the vertex shader (see buildProgram) parent->texcoordSize, // How many elements are there per texture coord? parent->texcoordType, // What is the type of this data in the array? GL_TRUE, // Do we want to normalize this data (0-1 range for fixed-point types) parent->texcoordSize*texcoordTypeSize, // What is the stride (i.e. bytes between texcoords)? 0); // What is the offset in the VBO to the texcoord data? glEnableVertexAttribArray(TEXCOORD_ATTRIB_IDX); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, parent->elementBufferName); #endif // Draw the object _HACKAROUND_GLDRAW_PRE(); glDrawElements(parent->primType, parent->numElements, parent->elementType, 0); GL_MAYBELOG("glhudparent render"); } void glhud_destroyDefault(GLModel *parent) { GLModelHUDElement *hudElement = (GLModelHUDElement *)parent->custom; if (!hudElement) { return; } if (hudElement->tpl) { FREE(hudElement->tpl); } if (hudElement->pixels) { FREE(hudElement->pixels); } FREE(parent->custom); } void glhud_screenToModel(const float x, const float y, const int screenW, const int screenH, float *centerX, float *centerY) { *centerX = (x/(screenW>>1))-1.f; *centerY = ((screenH-y)/(screenH>>1))-1.f; } void glhud_quadModelToScreen(const GLModel *model, const int screenW, const int screenH, float screenCoords[4]) { float x0 = 1.0; float y0 = 1.0; float x1 = -1.0; float y1 = -1.0; #warning NOTE: we possibly should use matrix calculations (but assuming HUD elements are identity/orthographic for now) GLfloat *positions = (GLfloat *)(model->positions); unsigned int stride = model->positionSize; unsigned int len = model->positionArraySize/getGLTypeSize(model->positionType); for (unsigned int i=0; i < len; i += stride) { float x = (positions[i] + 1.f) / 2.f; if (x < x0) { x0 = x; } if (x > x1) { x1 = x; } float y = (positions[i+1] + 1.f) / 2.f; LOG("\tmodel x:%f, y:%f", x, y); if (y < y0) { y0 = y; } if (y > y1) { y1 = y; } } // OpenGL screen origin is bottom-left (Android is top-left) float yFlip0 = screenH - (y1 * screenH); float yFlip1 = screenH - (y0 * screenH); screenCoords[0] = x0 * screenW; screenCoords[1] = yFlip0; screenCoords[2] = x1 * screenW; screenCoords[3] = yFlip1; } float glhud_getTimedVisibility(struct timespec timingBegin, float minAlpha, float maxAlpha) { struct timespec now = { 0 }; clock_gettime(CLOCK_MONOTONIC, &now); float alpha = minAlpha; struct timespec deltat = timespec_diff(timingBegin, now, NULL); if (deltat.tv_sec == 0) { alpha = maxAlpha; if (deltat.tv_nsec >= NANOSECONDS_PER_SECOND/2) { alpha -= ((float)deltat.tv_nsec-(NANOSECONDS_PER_SECOND/2)) / (float)(NANOSECONDS_PER_SECOND/2); if (alpha < 0) { alpha = 0; } } } return alpha; }