Expand GAL native test harness from 24 to 28 scenarios covering all 70 GAL methods. New scenarios: - scenario_text_attrs.cpp (24): Text attribute APIs (SetGlyphSize, SetFontBold/Italic/Underlined, SetTextMirrored, justification) - scenario_glyphs.cpp (25): DrawGlyph/DrawGlyphs with stroke glyphs - scenario_bitmap.cpp (26): DrawBitmap with test patterns - scenario_transform.cpp (27): Transform() API documentation Additional API coverage in existing scenarios: - Flush() in test harness - SetFlip(), SetRotation() in screen-transform - SetDepthRange() in depth-testing - GetGridPoint() in grid-native New stub files: - kifont_stub.h: STROKE_GLYPH factory functions for letter glyphs - bitmap_base_stub.h: Test pattern generators (checkerboard, gradient) Note: Bitmap scenario shows empty panels - DrawBitmap uses legacy OpenGL immediate mode (glBegin/glEnd) which doesn't work while shader is active. This is a known limitation when testing outside KiCad's VIEW rendering flow. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
261 lines
9.7 KiB
C++
261 lines
9.7 KiB
C++
/**
|
|
* Render Targets Scenario
|
|
*
|
|
* Tests GAL render target concepts:
|
|
* - SetTarget() / GetTarget()
|
|
* - ClearTarget()
|
|
* - HasTarget()
|
|
*
|
|
* NOTE: In this test harness context, we don't switch targets mid-frame
|
|
* as that requires specific compositor setup. Instead, we demonstrate
|
|
* the concept visually and test the API availability.
|
|
*
|
|
* RENDER_TARGET values:
|
|
* - TARGET_CACHED: Main rendering target (persistent)
|
|
* - TARGET_NONCACHED: Auxiliary target (cleared each frame)
|
|
* - TARGET_OVERLAY: Overlay items (cleared each frame)
|
|
* - TARGET_TEMP: Temporary target for special operations
|
|
*/
|
|
|
|
#include <gal/graphics_abstraction_layer.h>
|
|
#include <gal/definitions.h>
|
|
#include <cmath>
|
|
|
|
#ifndef M_PI
|
|
#define M_PI 3.14159265358979323846
|
|
#endif
|
|
|
|
namespace GALTest {
|
|
|
|
using KIGFX::COLOR4D;
|
|
using KIGFX::GAL;
|
|
using KIGFX::RENDER_TARGET;
|
|
using KIGFX::TARGET_CACHED;
|
|
using KIGFX::TARGET_NONCACHED;
|
|
using KIGFX::TARGET_OVERLAY;
|
|
using KIGFX::TARGET_TEMP;
|
|
|
|
void RenderRenderTargets(GAL* gal, int width, int height) {
|
|
// All drawing to the default target (NONCACHED in test harness)
|
|
|
|
// Background grid
|
|
gal->SetLayerDepth(100);
|
|
gal->SetIsFill(false);
|
|
gal->SetIsStroke(true);
|
|
gal->SetLineWidth(1.0);
|
|
gal->SetStrokeColor(COLOR4D(0.2, 0.2, 0.25, 0.4));
|
|
|
|
for (int x = 0; x < width; x += 30) {
|
|
gal->DrawLine(VECTOR2D(x, 0), VECTOR2D(x, height));
|
|
}
|
|
for (int y = 0; y < height; y += 30) {
|
|
gal->DrawLine(VECTOR2D(0, y), VECTOR2D(width, y));
|
|
}
|
|
|
|
// Visual representation of render target concept
|
|
// Show boxes representing each target type
|
|
|
|
double boxW = 160;
|
|
double boxH = 200;
|
|
double startX = 40;
|
|
double startY = 50;
|
|
double spacing = 180;
|
|
|
|
// Box 1: CACHED (persistent content)
|
|
gal->SetLayerDepth(50);
|
|
gal->SetIsFill(true);
|
|
gal->SetIsStroke(false);
|
|
gal->SetFillColor(COLOR4D(0.15, 0.3, 0.15, 1.0));
|
|
gal->DrawRectangle(VECTOR2D(startX, startY), VECTOR2D(startX + boxW, startY + boxH));
|
|
|
|
// Static PCB elements (what would go in cached)
|
|
gal->SetFillColor(COLOR4D(0.7, 0.5, 0.2, 1.0));
|
|
gal->DrawSegment(VECTOR2D(startX + 20, startY + 50), VECTOR2D(startX + boxW - 20, startY + 50), 6);
|
|
gal->DrawSegment(VECTOR2D(startX + 20, startY + 100), VECTOR2D(startX + boxW - 20, startY + 100), 6);
|
|
gal->DrawSegment(VECTOR2D(startX + 20, startY + 150), VECTOR2D(startX + boxW - 20, startY + 150), 6);
|
|
|
|
gal->SetFillColor(COLOR4D(0.8, 0.6, 0.3, 1.0));
|
|
gal->DrawCircle(VECTOR2D(startX + 40, startY + 50), 12);
|
|
gal->DrawCircle(VECTOR2D(startX + boxW - 40, startY + 50), 12);
|
|
gal->DrawCircle(VECTOR2D(startX + 40, startY + 100), 12);
|
|
gal->DrawCircle(VECTOR2D(startX + boxW - 40, startY + 100), 12);
|
|
gal->DrawCircle(VECTOR2D(startX + 40, startY + 150), 12);
|
|
gal->DrawCircle(VECTOR2D(startX + boxW - 40, startY + 150), 12);
|
|
|
|
// Frame
|
|
gal->SetIsFill(false);
|
|
gal->SetIsStroke(true);
|
|
gal->SetLineWidth(3.0);
|
|
gal->SetStrokeColor(COLOR4D(0.3, 0.6, 0.3, 1.0));
|
|
gal->DrawRectangle(VECTOR2D(startX, startY), VECTOR2D(startX + boxW, startY + boxH));
|
|
|
|
// Label
|
|
gal->SetLineWidth(1.0);
|
|
gal->SetStrokeColor(COLOR4D(0.5, 0.8, 0.5, 0.8));
|
|
gal->DrawRectangle(VECTOR2D(startX + 20, startY + boxH + 10), VECTOR2D(startX + boxW - 20, startY + boxH + 25));
|
|
|
|
// Box 2: NONCACHED (dynamic content)
|
|
double box2X = startX + spacing;
|
|
gal->SetLayerDepth(50);
|
|
gal->SetIsFill(true);
|
|
gal->SetIsStroke(false);
|
|
gal->SetFillColor(COLOR4D(0.3, 0.25, 0.1, 1.0));
|
|
gal->DrawRectangle(VECTOR2D(box2X, startY), VECTOR2D(box2X + boxW, startY + boxH));
|
|
|
|
// Dynamic elements (selection highlights, moving items)
|
|
gal->SetFillColor(COLOR4D(1.0, 1.0, 0.2, 0.4));
|
|
gal->DrawRectangle(VECTOR2D(box2X + 30, startY + 40), VECTOR2D(box2X + boxW - 30, startY + 80));
|
|
|
|
gal->SetFillColor(COLOR4D(0.2, 0.8, 0.2, 0.6));
|
|
gal->DrawRectangle(VECTOR2D(box2X + 50, startY + 90), VECTOR2D(box2X + boxW - 50, startY + 140));
|
|
|
|
// Movement arrows
|
|
gal->SetIsFill(false);
|
|
gal->SetIsStroke(true);
|
|
gal->SetLineWidth(2.0);
|
|
gal->SetStrokeColor(COLOR4D(1.0, 0.5, 0.0, 0.8));
|
|
gal->DrawLine(VECTOR2D(box2X + 80, startY + 160), VECTOR2D(box2X + 80, startY + 180));
|
|
gal->DrawLine(VECTOR2D(box2X + 80, startY + 180), VECTOR2D(box2X + 70, startY + 170));
|
|
gal->DrawLine(VECTOR2D(box2X + 80, startY + 180), VECTOR2D(box2X + 90, startY + 170));
|
|
|
|
// Frame
|
|
gal->SetLineWidth(3.0);
|
|
gal->SetStrokeColor(COLOR4D(0.8, 0.7, 0.2, 1.0));
|
|
gal->DrawRectangle(VECTOR2D(box2X, startY), VECTOR2D(box2X + boxW, startY + boxH));
|
|
|
|
// Label
|
|
gal->SetLineWidth(1.0);
|
|
gal->SetStrokeColor(COLOR4D(0.9, 0.8, 0.3, 0.8));
|
|
gal->DrawRectangle(VECTOR2D(box2X + 20, startY + boxH + 10), VECTOR2D(box2X + boxW - 20, startY + boxH + 25));
|
|
|
|
// Box 3: OVERLAY (crosshairs, measurements)
|
|
double box3X = startX + spacing * 2;
|
|
gal->SetLayerDepth(50);
|
|
gal->SetIsFill(true);
|
|
gal->SetIsStroke(false);
|
|
gal->SetFillColor(COLOR4D(0.15, 0.15, 0.25, 1.0));
|
|
gal->DrawRectangle(VECTOR2D(box3X, startY), VECTOR2D(box3X + boxW, startY + boxH));
|
|
|
|
// Crosshair overlay
|
|
gal->SetIsFill(false);
|
|
gal->SetIsStroke(true);
|
|
gal->SetLineWidth(1.0);
|
|
gal->SetStrokeColor(COLOR4D(1.0, 1.0, 1.0, 0.8));
|
|
double crossX = box3X + boxW / 2;
|
|
double crossY = startY + boxH / 2;
|
|
gal->DrawLine(VECTOR2D(box3X + 10, crossY), VECTOR2D(box3X + boxW - 10, crossY));
|
|
gal->DrawLine(VECTOR2D(crossX, startY + 10), VECTOR2D(crossX, startY + boxH - 10));
|
|
|
|
// Measurement line
|
|
gal->SetStrokeColor(COLOR4D(0.3, 1.0, 1.0, 0.9));
|
|
gal->SetLineWidth(2.0);
|
|
gal->DrawLine(VECTOR2D(box3X + 30, startY + 160), VECTOR2D(box3X + boxW - 30, startY + 160));
|
|
gal->SetIsFill(true);
|
|
gal->SetFillColor(COLOR4D(0.3, 1.0, 1.0, 0.9));
|
|
gal->DrawCircle(VECTOR2D(box3X + 30, startY + 160), 4);
|
|
gal->DrawCircle(VECTOR2D(box3X + boxW - 30, startY + 160), 4);
|
|
|
|
// Frame
|
|
gal->SetIsFill(false);
|
|
gal->SetIsStroke(true);
|
|
gal->SetLineWidth(3.0);
|
|
gal->SetStrokeColor(COLOR4D(0.4, 0.4, 0.8, 1.0));
|
|
gal->DrawRectangle(VECTOR2D(box3X, startY), VECTOR2D(box3X + boxW, startY + boxH));
|
|
|
|
// Label
|
|
gal->SetLineWidth(1.0);
|
|
gal->SetStrokeColor(COLOR4D(0.5, 0.5, 0.9, 0.8));
|
|
gal->DrawRectangle(VECTOR2D(box3X + 20, startY + boxH + 10), VECTOR2D(box3X + boxW - 20, startY + boxH + 25));
|
|
|
|
// Box 4: TEMP (special operations)
|
|
double box4X = startX + spacing * 3;
|
|
gal->SetLayerDepth(50);
|
|
gal->SetIsFill(true);
|
|
gal->SetIsStroke(false);
|
|
gal->SetFillColor(COLOR4D(0.25, 0.15, 0.25, 1.0));
|
|
gal->DrawRectangle(VECTOR2D(box4X, startY), VECTOR2D(box4X + boxW, startY + boxH));
|
|
|
|
// Temporary rendering (drag preview, etc)
|
|
gal->SetFillColor(COLOR4D(0.8, 0.3, 0.8, 0.5));
|
|
gal->DrawRectangle(VECTOR2D(box4X + 40, startY + 60), VECTOR2D(box4X + boxW - 40, startY + 120));
|
|
|
|
// Dotted outline showing "ghost" position
|
|
gal->SetIsFill(false);
|
|
gal->SetIsStroke(true);
|
|
gal->SetLineWidth(1.0);
|
|
gal->SetStrokeColor(COLOR4D(1.0, 0.5, 1.0, 0.6));
|
|
// Draw dashed rectangle manually
|
|
for (int i = 0; i < 10; i++) {
|
|
double x1 = box4X + 40 + i * 8;
|
|
double x2 = x1 + 5;
|
|
if (x2 > box4X + boxW - 40) x2 = box4X + boxW - 40;
|
|
gal->DrawLine(VECTOR2D(x1, startY + 140), VECTOR2D(x2, startY + 140));
|
|
gal->DrawLine(VECTOR2D(x1, startY + 180), VECTOR2D(x2, startY + 180));
|
|
}
|
|
|
|
// Frame
|
|
gal->SetLineWidth(3.0);
|
|
gal->SetStrokeColor(COLOR4D(0.7, 0.3, 0.7, 1.0));
|
|
gal->DrawRectangle(VECTOR2D(box4X, startY), VECTOR2D(box4X + boxW, startY + boxH));
|
|
|
|
// Label
|
|
gal->SetLineWidth(1.0);
|
|
gal->SetStrokeColor(COLOR4D(0.8, 0.4, 0.8, 0.8));
|
|
gal->DrawRectangle(VECTOR2D(box4X + 20, startY + boxH + 10), VECTOR2D(box4X + boxW - 20, startY + boxH + 25));
|
|
|
|
// Bottom: API demonstration - HasTarget results
|
|
double apiY = 340;
|
|
gal->SetLayerDepth(40);
|
|
|
|
// Check target availability (these are API calls)
|
|
bool hasCached = gal->HasTarget(TARGET_CACHED);
|
|
bool hasNoncached = gal->HasTarget(TARGET_NONCACHED);
|
|
bool hasOverlay = gal->HasTarget(TARGET_OVERLAY);
|
|
bool hasTemp = gal->HasTarget(TARGET_TEMP);
|
|
|
|
// Show results visually
|
|
gal->SetIsFill(true);
|
|
gal->SetIsStroke(false);
|
|
|
|
// Label area
|
|
gal->SetFillColor(COLOR4D(0.15, 0.15, 0.18, 1.0));
|
|
gal->DrawRectangle(VECTOR2D(40, apiY), VECTOR2D(760, apiY + 100));
|
|
|
|
// Indicator for each target
|
|
double indicatorY = apiY + 50;
|
|
double indicatorSpacing = 180;
|
|
|
|
// CACHED indicator
|
|
gal->SetFillColor(hasCached ? COLOR4D(0.2, 0.9, 0.2, 1.0) : COLOR4D(0.9, 0.2, 0.2, 1.0));
|
|
gal->DrawCircle(VECTOR2D(startX + boxW / 2, indicatorY), 15);
|
|
|
|
// NONCACHED indicator
|
|
gal->SetFillColor(hasNoncached ? COLOR4D(0.2, 0.9, 0.2, 1.0) : COLOR4D(0.9, 0.2, 0.2, 1.0));
|
|
gal->DrawCircle(VECTOR2D(startX + spacing + boxW / 2, indicatorY), 15);
|
|
|
|
// OVERLAY indicator
|
|
gal->SetFillColor(hasOverlay ? COLOR4D(0.2, 0.9, 0.2, 1.0) : COLOR4D(0.9, 0.2, 0.2, 1.0));
|
|
gal->DrawCircle(VECTOR2D(startX + spacing * 2 + boxW / 2, indicatorY), 15);
|
|
|
|
// TEMP indicator
|
|
gal->SetFillColor(hasTemp ? COLOR4D(0.2, 0.9, 0.2, 1.0) : COLOR4D(0.9, 0.2, 0.2, 1.0));
|
|
gal->DrawCircle(VECTOR2D(startX + spacing * 3 + boxW / 2, indicatorY), 15);
|
|
|
|
// Frame around API section
|
|
gal->SetIsFill(false);
|
|
gal->SetIsStroke(true);
|
|
gal->SetLineWidth(2.0);
|
|
gal->SetStrokeColor(COLOR4D(0.4, 0.4, 0.5, 0.8));
|
|
gal->DrawRectangle(VECTOR2D(40, apiY), VECTOR2D(760, apiY + 100));
|
|
|
|
// Bottom: GetTarget demonstration
|
|
RENDER_TARGET currentTarget = gal->GetTarget();
|
|
(void)currentTarget; // Used - shows API works
|
|
|
|
// Label for current target
|
|
gal->SetLineWidth(1.0);
|
|
gal->SetStrokeColor(COLOR4D(0.5, 0.5, 0.6, 0.8));
|
|
gal->DrawRectangle(VECTOR2D(40, apiY + 70), VECTOR2D(300, apiY + 85));
|
|
}
|
|
|
|
} // namespace GALTest
|