/* Copyright (c) 2022 Alex Diener This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Alex Diener alex@ludobloom.com */ #include "uitoolkit/UIDrawingInterface2DMultitexture.h" #include "uitoolkit/UIToolkitDrawing.h" #define stemobject_implementation UIDrawingInterface2DMultitexture stemobject_vtable_begin(); stemobject_vtable_entry(dispose); stemobject_vtable_entry(drawQuad); stemobject_vtable_entry(drawQuadWithTransform); stemobject_vtable_entry(drawQuadWithSeparateColors); stemobject_vtable_entry(drawSlicedQuad3x3); stemobject_vtable_entry(drawSlicedQuad3x1); stemobject_vtable_entry(drawSlicedQuad1x3); stemobject_vtable_entry(drawSlicedQuad3x3WithAlternatingRowColors); stemobject_vtable_entry(drawIndexedVertices_p2f_t2f_c4f); stemobject_vtable_entry(drawString); stemobject_vtable_entry(drawTextLayout); stemobject_vtable_end(); UIDrawingInterface2DMultitexture * UIDrawingInterface2DMultitexture_create(unsigned int uiTextureIndex) { stemobject_create_implementation(init, uiTextureIndex) } bool UIDrawingInterface2DMultitexture_init(UIDrawingInterface2DMultitexture * self, unsigned int uiTextureIndex) { call_super(init, self); self->uiTextureIndex = uiTextureIndex; self->typefaceTable = HashTable_create(sizeof(int)); return true; } void UIDrawingInterface2DMultitexture_dispose(UIDrawingInterface2DMultitexture * self) { HashTable_dispose(self->typefaceTable); call_super_virtual(dispose, self); } void UIDrawingInterface2DMultitexture_registerTypeface(UIDrawingInterface2DMultitexture * self, compat_type(UITypeface *) typeface, int textureIndex) { HashTable_set(self->typefaceTable, HashTable_pointerKey(typeface), &textureIndex); } void UIDrawingInterface2DMultitexture_unregisterTypeface(UIDrawingInterface2DMultitexture * self, compat_type(UITypeface *) typeface) { HashTable_delete(self->typefaceTable, HashTable_pointerKey(typeface)); } void UIDrawingInterface2DMultitexture_drawQuad(UIDrawingInterface2DMultitexture * self, Rect4f vertexBounds, Rect4f textureBounds, Color4f color, VertexIO * vertexIO) { writeQuad2DMultitexture(vertexBounds, textureBounds, color, self->uiTextureIndex, vertexIO); } void UIDrawingInterface2DMultitexture_drawQuadWithTransform(UIDrawingInterface2DMultitexture * self, Rect4f vertexBounds, Rect4f textureBounds, quadTransformBits transformBits, Color4f color, VertexIO * vertexIO) { writeQuad2DMultitextureWithTransform(vertexBounds, textureBounds, color, self->uiTextureIndex, transformBits, vertexIO); } void UIDrawingInterface2DMultitexture_drawQuadWithSeparateColors(UIDrawingInterface2DMultitexture * self, Rect4f vertexBounds, Rect4f textureBounds, Color4f colorBottomLeft, Color4f colorBottomRight, Color4f colorTopLeft, Color4f colorTopRight, VertexIO * vertexIO) { struct vertex_p2f_t2f_c4f_i1f vertices[4], vertex; vertex.position[0] = vertexBounds.xMin; vertex.position[1] = vertexBounds.yMin; vertex.texCoords[0] = textureBounds.xMin; vertex.texCoords[1] = textureBounds.yMin; vertex.color[0] = colorBottomLeft.red; vertex.color[1] = colorBottomLeft.green; vertex.color[2] = colorBottomLeft.blue; vertex.color[3] = colorBottomLeft.alpha; vertex.textureIndex = self->uiTextureIndex; vertices[0] = vertex; vertex.position[0] = vertexBounds.xMax; vertex.texCoords[0] = textureBounds.xMax; vertex.color[0] = colorBottomRight.red; vertex.color[1] = colorBottomRight.green; vertex.color[2] = colorBottomRight.blue; vertex.color[3] = colorBottomRight.alpha; vertices[1] = vertex; vertex.position[1] = vertexBounds.yMax; vertex.texCoords[1] = textureBounds.yMax; vertex.color[0] = colorTopRight.red; vertex.color[1] = colorTopRight.green; vertex.color[2] = colorTopRight.blue; vertex.color[3] = colorTopRight.alpha; vertices[2] = vertex; vertex.position[0] = vertexBounds.xMin; vertex.texCoords[0] = textureBounds.xMin; vertex.color[0] = colorTopLeft.red; vertex.color[1] = colorTopLeft.green; vertex.color[2] = colorTopLeft.blue; vertex.color[3] = colorTopLeft.alpha; vertices[3] = vertex; uint32_t indexes[6] = {0, 1, 2, 2, 3, 0}; VertexIO_writeIndexedVertices(vertexIO, 4, vertices, 6, indexes); } #define copySlicedVertices(count) \ for (unsigned int vertexIndex = 0; vertexIndex < count; vertexIndex++) { \ vertices[vertexIndex].position[0] = sliceVertices[vertexIndex].position.x; \ vertices[vertexIndex].position[1] = sliceVertices[vertexIndex].position.y; \ vertices[vertexIndex].texCoords[0] = sliceVertices[vertexIndex].texCoords.x; \ vertices[vertexIndex].texCoords[1] = sliceVertices[vertexIndex].texCoords.y; \ vertices[vertexIndex].color[0] = color.red; \ vertices[vertexIndex].color[1] = color.green; \ vertices[vertexIndex].color[2] = color.blue; \ vertices[vertexIndex].color[3] = color.alpha; \ vertices[vertexIndex].textureIndex = self->uiTextureIndex; \ } void UIDrawingInterface2DMultitexture_drawSlicedQuad3x3(UIDrawingInterface2DMultitexture * self, Rect4f vertexBounds, Rect4f textureBounds, UISliceGrid3x3 slices, Color4f color, VertexIO * vertexIO) { struct vertex_p2f_t2f_c4f_i1f vertices[UI_SLICE_VERTEX_COUNT_3x3]; UIToolkit_sliceVertex sliceVertices[UI_SLICE_VERTEX_COUNT_3x3]; uint32_t indexes[UI_SLICE_INDEX_COUNT_3x3]; UIToolkit_sliceQuad3x3(vertexBounds, textureBounds, slices, sliceVertices, indexes); copySlicedVertices(UI_SLICE_VERTEX_COUNT_3x3); VertexIO_writeIndexedVertices(vertexIO, UI_SLICE_VERTEX_COUNT_3x3, vertices, UI_SLICE_INDEX_COUNT_3x3, indexes); } void UIDrawingInterface2DMultitexture_drawSlicedQuad3x1(UIDrawingInterface2DMultitexture * self, Rect4f vertexBounds, Rect4f textureBounds, UISliceGrid3x1 slices, Color4f color, VertexIO * vertexIO) { struct vertex_p2f_t2f_c4f_i1f vertices[UI_SLICE_VERTEX_COUNT_3x1]; UIToolkit_sliceVertex sliceVertices[UI_SLICE_VERTEX_COUNT_3x1]; uint32_t indexes[UI_SLICE_INDEX_COUNT_3x1]; UIToolkit_sliceQuad3x1(vertexBounds, textureBounds, slices, sliceVertices, indexes); copySlicedVertices(UI_SLICE_VERTEX_COUNT_3x1); VertexIO_writeIndexedVertices(vertexIO, UI_SLICE_VERTEX_COUNT_3x1, vertices, UI_SLICE_INDEX_COUNT_3x1, indexes); } void UIDrawingInterface2DMultitexture_drawSlicedQuad1x3(UIDrawingInterface2DMultitexture * self, Rect4f vertexBounds, Rect4f textureBounds, UISliceGrid1x3 slices, Color4f color, VertexIO * vertexIO) { struct vertex_p2f_t2f_c4f_i1f vertices[UI_SLICE_VERTEX_COUNT_1x3]; UIToolkit_sliceVertex sliceVertices[UI_SLICE_VERTEX_COUNT_1x3]; uint32_t indexes[UI_SLICE_INDEX_COUNT_1x3]; UIToolkit_sliceQuad1x3(vertexBounds, textureBounds, slices, sliceVertices, indexes); copySlicedVertices(UI_SLICE_VERTEX_COUNT_1x3); VertexIO_writeIndexedVertices(vertexIO, UI_SLICE_VERTEX_COUNT_1x3, vertices, UI_SLICE_INDEX_COUNT_1x3, indexes); } void UIDrawingInterface2DMultitexture_drawSlicedQuad3x3WithAlternatingRowColors(UIDrawingInterface2DMultitexture * self, Rect4f vertexBounds, Rect4f textureBounds, UISliceGrid3x3 slices, float inset, float rowHeight, Color4f evenColor, Color4f oddColor, VertexIO * vertexIO) { unsigned int vertexCount, indexCount; UIToolkit_getCountsForSlicedQuad3x3WithRows(vertexBounds, slices, inset, rowHeight, &vertexCount, &indexCount); struct vertex_p2f_t2f_c4f_i1f vertices[vertexCount]; UIToolkit_sliceVertexWithRow sliceVertices[vertexCount]; uint32_t indexes[indexCount]; UIToolkit_sliceQuad3x3WithRows(vertexBounds, textureBounds, slices, inset, rowHeight, sliceVertices, indexes); Color4f colors[2] = {evenColor, oddColor}; for (unsigned int vertexIndex = 0; vertexIndex < vertexCount; vertexIndex++) { vertices[vertexIndex].position[0] = sliceVertices[vertexIndex].position.x; vertices[vertexIndex].position[1] = sliceVertices[vertexIndex].position.y; vertices[vertexIndex].texCoords[0] = sliceVertices[vertexIndex].texCoords.x; vertices[vertexIndex].texCoords[1] = sliceVertices[vertexIndex].texCoords.y; Color4f color = colors[sliceVertices[vertexIndex].rowIndex & 0x1]; vertices[vertexIndex].color[0] = color.red; vertices[vertexIndex].color[1] = color.green; vertices[vertexIndex].color[2] = color.blue; vertices[vertexIndex].color[3] = color.alpha; vertices[vertexIndex].textureIndex = self->uiTextureIndex; } VertexIO_writeIndexedVertices(vertexIO, vertexCount, vertices, indexCount, indexes); } void UIDrawingInterface2DMultitexture_drawIndexedVertices_p2f_t2f_c4f(UIDrawingInterface2DMultitexture * self, unsigned int vertexCount, const struct vertex_p2f_t2f_c4f * vertices, unsigned int indexCount, const uint32_t * indexes, VertexIO * vertexIO) { struct vertex_p2f_t2f_c4f_i1f verticesTranslated[vertexCount]; for (unsigned int vertexIndex = 0; vertexIndex < vertexCount; vertexIndex++) { verticesTranslated[vertexIndex].position[0] = vertices[vertexIndex].position[0]; verticesTranslated[vertexIndex].position[1] = vertices[vertexIndex].position[1]; verticesTranslated[vertexIndex].texCoords[0] = vertices[vertexIndex].texCoords[0]; verticesTranslated[vertexIndex].texCoords[1] = vertices[vertexIndex].texCoords[1]; verticesTranslated[vertexIndex].color[0] = vertices[vertexIndex].color[0]; verticesTranslated[vertexIndex].color[1] = vertices[vertexIndex].color[1]; verticesTranslated[vertexIndex].color[2] = vertices[vertexIndex].color[2]; verticesTranslated[vertexIndex].color[3] = vertices[vertexIndex].color[3]; verticesTranslated[vertexIndex].textureIndex = self->uiTextureIndex; } VertexIO_writeIndexedVertices(vertexIO, vertexCount, verticesTranslated, indexCount, indexes); } struct drawStringContext { Color4f color; Color4f fixedColor; unsigned int textureIndex; VertexIO * vertexIO; }; static void glyphCallback(Rect4f vertexBounds, Rect4f textureBounds, bool fixedColor, void * context) { struct drawStringContext * contextStruct = context; Color4f color = fixedColor ? contextStruct->fixedColor : contextStruct->color; struct vertex_p2f_t2f_c4f_i1f vertices[4], vertex = {{vertexBounds.xMin, vertexBounds.yMin}, {textureBounds.xMin, textureBounds.yMin}, {color.red, color.green, color.blue, color.alpha}, contextStruct->textureIndex}; vertices[0] = vertex; vertex.position[0] = vertexBounds.xMax; vertex.texCoords[0] = textureBounds.xMax; vertices[1] = vertex; vertex.position[1] = vertexBounds.yMax; vertex.texCoords[1] = textureBounds.yMax; vertices[2] = vertex; vertex.position[0] = vertexBounds.xMin; vertex.texCoords[0] = textureBounds.xMin; vertices[3] = vertex; uint32_t indexes[6] = {0, 1, 2, 2, 3, 0}; VertexIO_writeIndexedVertices(contextStruct->vertexIO, 4, vertices, 6, indexes); } void UIDrawingInterface2DMultitexture_drawString(UIDrawingInterface2DMultitexture * self, compat_type(UITypeface *) typefaceUntyped, String string, Vector2f offset, Vector2f relativeOrigin, float textScale, Color4f color, VertexIO * vertexIO) { struct drawStringContext contextStruct = {.color = color, .textureIndex = self->uiTextureIndex, .vertexIO = vertexIO}; if (self->alphaPremultiplied) { contextStruct.fixedColor = COLOR4f(color.alpha, color.alpha, color.alpha, color.alpha); } else { contextStruct.fixedColor = COLOR4f(1.0f, 1.0f, 1.0f, color.alpha); } UITypeface * typeface = typefaceUntyped; int * typefaceEntry = HashTable_get(self->typefaceTable, HashTable_pointerKey(typeface)); if (typefaceEntry != NULL) { contextStruct.textureIndex = *typefaceEntry; } if (relativeOrigin.x != 0.0f) { offset.x -= call_virtual(measureString, typeface, string) * relativeOrigin.x * textScale; } if (relativeOrigin.y != 1.0f) { offset.y += call_virtual(getLineHeight, typeface) * (1.0f - relativeOrigin.y) * textScale; } call_virtual(writeGlyphsWithCallback, typeface, string, offset, textScale, glyphCallback, &contextStruct); } void UIDrawingInterface2DMultitexture_drawTextLayout(UIDrawingInterface2DMultitexture * self, compat_type(UITextLayout *) textLayoutUntyped, Vector2f offset, Vector2f relativeOrigin, float textScale, Color4f color, VertexIO * vertexIO) { struct drawStringContext contextStruct = {.color = color, .textureIndex = self->uiTextureIndex, .vertexIO = vertexIO}; if (self->alphaPremultiplied) { contextStruct.fixedColor = COLOR4f(color.alpha, color.alpha, color.alpha, color.alpha); } else { contextStruct.fixedColor = COLOR4f(1.0f, 1.0f, 1.0f, color.alpha); } UITextLayout * textLayout = textLayoutUntyped; int * typefaceEntry = HashTable_get(self->typefaceTable, HashTable_pointerKey(textLayout->typeface)); if (typefaceEntry != NULL) { contextStruct.textureIndex = *typefaceEntry; } if (relativeOrigin.x != 0.0f || relativeOrigin.y != 1.0f) { Vector2f size = call_virtual(measureString, textLayout); offset.x -= size.x * relativeOrigin.x * textScale; offset.y += size.y * (1.0f - relativeOrigin.y) * textScale; } call_virtual(writeGlyphsWithCallback, textLayout, offset, textScale, glyphCallback, &contextStruct); }