celestia/src/celengine/overlay.cpp

296 lines
6.4 KiB
C++

// overlay.cpp
//
// Copyright (C) 2001, Chris Laurel <claurel@shatters.net>
//
// 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 2
// of the License, or (at your option) any later version.
#include <cstring>
#include <iostream>
#include <Eigen/Core>
#include <celmath/geomutil.h>
#include <celttf/truetypefont.h>
#include <celutil/color.h>
#include <celutil/utf8.h>
#include "overlay.h"
#include "rectangle.h"
#include "render.h"
#include "texture.h"
#include "vecgl.h"
using namespace std;
using namespace Eigen;
using namespace celmath;
Overlay::Overlay(Renderer& r) :
ostream(&sbuf),
renderer(r)
{
sbuf.setOverlay(this);
}
void Overlay::begin()
{
projection = Ortho2D(0.0f, (float)windowWidth, 0.0f, (float)windowHeight);
// ModelView is Identity
renderer.enableBlending();
renderer.setBlendingFactors(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
global.reset();
useTexture = false;
}
void Overlay::end()
{
}
void Overlay::setWindowSize(int w, int h)
{
windowWidth = w;
windowHeight = h;
}
void Overlay::setFont(const std::shared_ptr<TextureFont>& f)
{
if (f != font)
{
if (font != nullptr)
font->flush();
font = f;
fontChanged = true;
}
}
void Overlay::beginText()
{
savePos();
textBlock++;
if (font != nullptr)
{
font->bind();
font->setMVPMatrices(projection);
useTexture = true;
fontChanged = false;
}
}
void Overlay::endText()
{
if (textBlock > 0)
{
textBlock--;
restorePos();
}
font->unbind();
}
void Overlay::print(wchar_t c)
{
if (font != nullptr)
{
if (!useTexture || fontChanged)
{
font->bind();
font->setMVPMatrices(projection);
useTexture = true;
fontChanged = false;
}
switch (c)
{
case '\n':
if (textBlock > 0)
{
restorePos();
global.y -= 1.0f + font->getHeight();
savePos();
}
break;
default:
font->render(c, global.x + xoffset, global.y + yoffset);
xoffset += font->getAdvance(c);
break;
}
}
}
void Overlay::print(char c)
{
if (font != nullptr)
{
if (!useTexture || fontChanged)
{
font->bind();
font->setMVPMatrices(projection);
useTexture = true;
fontChanged = false;
}
switch (c)
{
case '\n':
if (textBlock > 0)
{
restorePos();
global.y -= 1.0f + font->getHeight();
savePos();
}
break;
default:
font->render(c, global.x + xoffset, global.y + yoffset);
xoffset += font->getAdvance(c);
break;
}
}
}
void Overlay::print_impl(const std::string& s)
{
size_t length = s.length();
bool validChar = true;
size_t i = 0;
while (i < length && validChar)
{
wchar_t ch = 0;
validChar = UTF8Decode(s.c_str(), i, length, ch);
i += UTF8EncodedSize(ch);
print(ch);
}
}
void Overlay::drawRectangle(const celestia::Rect& r)
{
if (useTexture && r.tex == nullptr)
useTexture = false;
renderer.drawRectangle(r, ShaderProperties::FisheyeOverrideModeDisabled, projection);
}
void Overlay::setColor(float r, float g, float b, float a)
{
if (font != nullptr)
font->flush();
glVertexAttrib4f(CelestiaGLProgram::ColorAttributeIndex, r, g, b, a);
}
void Overlay::setColor(const Color& c)
{
if (font != nullptr)
font->flush();
glVertexAttrib4f(CelestiaGLProgram::ColorAttributeIndex,
c.red(), c.green(), c.blue(), c.alpha());
}
void Overlay::moveBy(float dx, float dy)
{
global.x += dx;
global.y += dy;
}
void Overlay::savePos()
{
posStack.push_back(global);
}
void Overlay::restorePos()
{
if (!posStack.empty())
{
global = posStack.back();
posStack.pop_back();
}
xoffset = yoffset = 0.0f;
}
//
// OverlayStreamBuf implementation
//
OverlayStreamBuf::OverlayStreamBuf()
{
setbuf(nullptr, 0);
}
void OverlayStreamBuf::setOverlay(Overlay* o)
{
overlay = o;
}
int OverlayStreamBuf::overflow(int c)
{
if (overlay != nullptr)
{
switch (decodeState)
{
case UTF8DecodeStart:
if (c < 0x80)
{
// Just a normal 7-bit character
overlay->print((char) c);
}
else
{
unsigned int count;
if ((c & 0xe0) == 0xc0)
count = 2;
else if ((c & 0xf0) == 0xe0)
count = 3;
else if ((c & 0xf8) == 0xf0)
count = 4;
else if ((c & 0xfc) == 0xf8)
count = 5;
else if ((c & 0xfe) == 0xfc)
count = 6;
else
count = 1; // Invalid byte
if (count > 1)
{
unsigned int mask = (1 << (7 - count)) - 1;
decodeShift = (count - 1) * 6;
decodedChar = (c & mask) << decodeShift;
decodeState = UTF8DecodeMultibyte;
}
else
{
// If the character isn't valid multibyte sequence head,
// silently skip it by leaving the decoder state alone.
}
}
break;
case UTF8DecodeMultibyte:
if ((c & 0xc0) == 0x80)
{
// We have a valid non-head byte in the sequence
decodeShift -= 6;
decodedChar |= (c & 0x3f) << decodeShift;
if (decodeShift == 0)
{
overlay->print(decodedChar);
decodeState = UTF8DecodeStart;
}
}
else
{
// Bad byte in UTF-8 encoded sequence; we'll silently ignore
// it and reset the state of the UTF-8 decoder.
decodeState = UTF8DecodeStart;
}
break;
}
}
return c;
}