1
0
Fork 0

Compare commits

...

7 Commits

Author SHA1 Message Date
drelbszoomer ecccf4160d
Merge 7edc18c977 into b82ffa3542 2025-07-20 21:58:54 +00:00
Peter Nelson b82ffa3542
Codechange: Decouple glyph map from SpriteFontCache instances. (#14449)
This makes the map independent from the SpriteFontCache instances.
2025-07-20 22:58:43 +01:00
Peter Nelson 8e2df7809b
Codechange: Add distinct type to hold pixel drawing colour. (#14457)
This is used for individual pixels as well as line drawing.
2025-07-20 22:57:55 +01:00
Jonathan G Rennison 821784004d Fix: [Linkgraph] Incorrect NodeID to StationID conversion for EraseFlows 2025-07-20 16:07:11 +02:00
Jonathan G Rennison f0447d59d4 Codechange: Use StationID as StationIDStack Titem type 2025-07-20 16:06:03 +02:00
Jonathan G Rennison cbdd358ae8 Codechange: Allow SmallStack Titem type to be non-structural 2025-07-20 16:06:03 +02:00
drelbszoomer 7edc18c977 Feature: Add ALSA midi output support 2025-02-20 22:47:39 -05:00
81 changed files with 10789 additions and 316 deletions

View File

@ -144,6 +144,7 @@ if(NOT OPTION_DEDICATED)
find_package(Freetype) find_package(Freetype)
find_package(SDL2) find_package(SDL2)
find_package(Fluidsynth) find_package(Fluidsynth)
find_package(ALSA)
if(Freetype_FOUND) if(Freetype_FOUND)
find_package(Fontconfig) find_package(Fontconfig)
endif() endif()
@ -326,6 +327,7 @@ if(NOT OPTION_DEDICATED)
link_package(ICU_i18n) link_package(ICU_i18n)
link_package(ICU_uc) link_package(ICU_uc)
link_package(OpusFile TARGET OpusFile::opusfile) link_package(OpusFile TARGET OpusFile::opusfile)
link_package(ALSA)
if(SDL2_FOUND AND OPENGL_FOUND AND UNIX) if(SDL2_FOUND AND OPENGL_FOUND AND UNIX)
# SDL2 dynamically loads OpenGL if needed, so do not link to OpenGL when # SDL2 dynamically loads OpenGL if needed, so do not link to OpenGL when

View File

@ -0,0 +1,76 @@
#[=======================================================================[.rst:
FindALSA
--------
Find Advanced Linux Sound Architecture (ALSA)
Find the alsa libraries (``asound``)
IMPORTED Targets
^^^^^^^^^^^^^^^^
.. versionadded:: 3.12
This module defines :prop_tgt:`IMPORTED` target ``ALSA::ALSA``, if
ALSA has been found.
Result Variables
^^^^^^^^^^^^^^^^
This module defines the following variables:
``ALSA_FOUND``
True if ALSA_INCLUDE_DIR & ALSA_LIBRARY are found
``ALSA_LIBRARIES``
List of libraries when using ALSA.
``ALSA_INCLUDE_DIRS``
Where to find the ALSA headers.
Cache variables
^^^^^^^^^^^^^^^
The following cache variables may also be set:
``ALSA_INCLUDE_DIR``
the ALSA include directory
``ALSA_LIBRARY``
the absolute path of the asound library
#]=======================================================================]
find_package(PkgConfig QUIET)
pkg_check_modules(PC_ALSA QUIET alsa)
find_path(ALSA_INCLUDE_DIR NAMES alsa/asoundlib.h
DOC "The ALSA (asound) include directory"
)
find_library(ALSA_LIBRARY NAMES asound
DOC "The ALSA (asound) library"
)
if(ALSA_INCLUDE_DIR AND EXISTS "${ALSA_INCLUDE_DIR}/alsa/version.h")
file(STRINGS "${ALSA_INCLUDE_DIR}/alsa/version.h" alsa_version_str REGEX "^#define[\t ]+SND_LIB_VERSION_STR[\t ]+\".*\"")
string(REGEX REPLACE "^.*SND_LIB_VERSION_STR[\t ]+\"([^\"]*)\".*$" "\\1" ALSA_VERSION_STRING "${alsa_version_str}")
unset(alsa_version_str)
endif()
include(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(ALSA
REQUIRED_VARS ALSA_LIBRARY ALSA_INCLUDE_DIR
VERSION_VAR ALSA_VERSION_STRING)
if(ALSA_FOUND)
set( ALSA_LIBRARIES ${ALSA_LIBRARY} )
set( ALSA_INCLUDE_DIRS ${ALSA_INCLUDE_DIR} )
if(NOT TARGET ALSA::ALSA)
add_library(ALSA::ALSA UNKNOWN IMPORTED)
set_target_properties(ALSA::ALSA PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${ALSA_INCLUDE_DIRS}")
set_property(TARGET ALSA::ALSA APPEND PROPERTY IMPORTED_LOCATION "${ALSA_LIBRARY}")
endif()
endif()
mark_as_advanced(ALSA_INCLUDE_DIR ALSA_LIBRARY)

View File

@ -7,3 +7,4 @@ add_subdirectory(squirrel)
add_subdirectory(nlohmann) add_subdirectory(nlohmann)
add_subdirectory(opengl) add_subdirectory(opengl)
add_subdirectory(openttd_social_integration_api) add_subdirectory(openttd_social_integration_api)
add_subdirectory(midifile)

2012
src/3rdparty/midifile/Binasc.cpp vendored 100644

File diff suppressed because it is too large Load Diff

161
src/3rdparty/midifile/Binasc.h vendored 100644
View File

@ -0,0 +1,161 @@
//
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Mon Feb 16 12:26:32 PST 2015 Adapted from binasc program.
// Last Modified: Sat Apr 21 10:52:19 PDT 2018 Removed using namespace std;
// Filename: midifile/include/Binasc.h
// Website: http://midifile.sapp.org
// Syntax: C++11
// vim: ts=3 noexpandtab
//
// description: Interface to convert bytes between binary and ASCII forms.
//
#ifndef _BINASC_H_INCLUDED
#define _BINASC_H_INCLUDED
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <string>
namespace smf {
typedef unsigned char uchar;
typedef unsigned short ushort;
typedef unsigned long ulong;
class Binasc {
public:
Binasc (void);
~Binasc ();
// functions for setting options:
int setLineLength (int length);
int getLineLength (void);
int setLineBytes (int length);
int getLineBytes (void);
void setComments (int state);
void setCommentsOn (void);
void setCommentsOff (void);
int getComments (void);
void setBytes (int state);
void setBytesOn (void);
void setBytesOff (void);
int getBytes (void);
void setMidi (int state);
void setMidiOn (void);
void setMidiOff (void);
int getMidi (void);
// functions for converting into a binary file:
int writeToBinary (const std::string& outfile,
const std::string& infile);
int writeToBinary (const std::string& outfile,
std::istream& input);
int writeToBinary (std::ostream& out,
const std::string& infile);
int writeToBinary (std::ostream& out,
std::istream& input);
// functions for converting into an ASCII file with hex bytes:
int readFromBinary (const std::string&
outfile,
const std::string& infile);
int readFromBinary (const std::string& outfile,
std::istream& input);
int readFromBinary (std::ostream& out,
const std::string& infile);
int readFromBinary (std::ostream& out,
std::istream& input);
// static functions for writing ordered bytes:
static std::ostream& writeLittleEndianUShort (std::ostream& out,
ushort value);
static std::ostream& writeBigEndianUShort (std::ostream& out,
ushort value);
static std::ostream& writeLittleEndianShort (std::ostream& out,
short value);
static std::ostream& writeBigEndianShort (std::ostream& out,
short value);
static std::ostream& writeLittleEndianULong (std::ostream& out,
ulong value);
static std::ostream& writeBigEndianULong (std::ostream& out,
ulong value);
static std::ostream& writeLittleEndianLong (std::ostream& out,
long value);
static std::ostream& writeBigEndianLong (std::ostream& out,
long value);
static std::ostream& writeLittleEndianFloat (std::ostream& out,
float value);
static std::ostream& writeBigEndianFloat (std::ostream& out,
float value);
static std::ostream& writeLittleEndianDouble (std::ostream& out,
double value);
static std::ostream& writeBigEndianDouble (std::ostream& out,
double value);
static std::string keyToPitchName (int key);
protected:
int m_bytesQ; // option for printing hex bytes in ASCII output.
int m_commentsQ; // option for printing comments in ASCII output.
int m_midiQ; // output ASCII data as parsed MIDI file.
int m_maxLineLength;// number of character in ASCII output on a line.
int m_maxLineBytes; // number of hex bytes in ASCII output on a line.
private:
// helper functions for reading ASCII content to conver to binary:
int processLine (std::ostream& out,
const std::string& input,
int lineNum);
int processAsciiWord (std::ostream& out,
const std::string& input,
int lineNum);
int processStringWord (std::ostream& out,
const std::string& input,
int lineNum);
int processBinaryWord (std::ostream& out,
const std::string& input,
int lineNum);
int processDecimalWord (std::ostream& out,
const std::string& input,
int lineNum);
int processHexWord (std::ostream& out,
const std::string& input,
int lineNum);
int processVlvWord (std::ostream& out,
const std::string& input,
int lineNum);
int processMidiPitchBendWord(std::ostream& out,
const std::string& input,
int lineNum);
int processMidiTempoWord (std::ostream& out,
const std::string& input,
int lineNum);
// helper functions for reading binary content to convert to ASCII:
int outputStyleAscii (std::ostream& out, std::istream& input);
int outputStyleBinary (std::ostream& out, std::istream& input);
int outputStyleBoth (std::ostream& out, std::istream& input);
int outputStyleMidi (std::ostream& out, std::istream& input);
// MIDI parsing helper functions:
int readMidiEvent (std::ostream& out, std::istream& infile,
int& trackbytes, int& command);
int getVLV (std::istream& infile, int& trackbytes);
int getWord (std::string& word, const std::string& input,
const std::string& terminators, int index);
static const char *GMinstrument[128];
};
} // end of namespace smf
#endif /* _BINASC_H_INCLUDED */

View File

@ -0,0 +1,12 @@
add_files(
Binasc.cpp
Binasc.h
MidiEvent.cpp
MidiEvent.h
MidiEventList.cpp
MidiEventList.h
MidiFile.cpp
MidiFile.h
MidiMessage.cpp
MidiMessage.h
)

View File

@ -0,0 +1,22 @@
Copyright (c) 1999-2018, Craig Stuart Sapp
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
and the following disclaimer in the documentation and/or other materials
provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,298 @@
//
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Sat Feb 14 21:40:14 PST 2015
// Last Modified: Sat Apr 21 10:52:19 PDT 2018 Removed using namespace std;
// Filename: midifile/src/MidiEvent.cpp
// Website: http://midifile.sapp.org
// Syntax: C++11
// vim: ts=3 noexpandtab
//
// Description: A class which stores a MidiMessage and a timestamp
// for the MidiFile class.
//
#include "MidiEvent.h"
#include <cstdlib>
namespace smf {
//////////////////////////////
//
// MidiEvent::MidiEvent -- Constructor classes
//
MidiEvent::MidiEvent(void) : MidiMessage() {
clearVariables();
}
MidiEvent::MidiEvent(int command) : MidiMessage(command) {
clearVariables();
}
MidiEvent::MidiEvent(int command, int p1) : MidiMessage(command, p1) {
clearVariables();
}
MidiEvent::MidiEvent(int command, int p1, int p2)
: MidiMessage(command, p1, p2) {
clearVariables();
}
MidiEvent::MidiEvent(int aTime, int aTrack, vector<uchar>& message)
: MidiMessage(message) {
track = aTrack;
tick = aTime;
seconds = 0.0;
seq = 0;
m_eventlink = NULL;
}
MidiEvent::MidiEvent(const MidiEvent& mfevent) : MidiMessage() {
track = mfevent.track;
tick = mfevent.tick;
seconds = mfevent.seconds;
seq = mfevent.seq;
m_eventlink = NULL;
this->resize(mfevent.size());
for (int i=0; i<(int)this->size(); i++) {
(*this)[i] = mfevent[i];
}
}
//////////////////////////////
//
// MidiEvent::~MidiEvent -- MidiFile Event destructor
//
MidiEvent::~MidiEvent() {
track = -1;
tick = -1;
seconds = -1.0;
seq = -1;
this->resize(0);
m_eventlink = NULL;
}
//////////////////////////////
//
// MidiEvent::clearVariables -- Clear everything except MidiMessage data.
//
void MidiEvent::clearVariables(void) {
track = 0;
tick = 0;
seconds = 0.0;
seq = 0;
m_eventlink = NULL;
}
//////////////////////////////
//
// MidiEvent::operator= -- Copy the contents of another MidiEvent.
//
MidiEvent& MidiEvent::operator=(const MidiEvent& mfevent) {
if (this == &mfevent) {
return *this;
}
tick = mfevent.tick;
track = mfevent.track;
seconds = mfevent.seconds;
seq = mfevent.seq;
m_eventlink = NULL;
this->resize(mfevent.size());
for (int i=0; i<(int)this->size(); i++) {
(*this)[i] = mfevent[i];
}
return *this;
}
MidiEvent& MidiEvent::operator=(const MidiMessage& message) {
if (this == &message) {
return *this;
}
clearVariables();
this->resize(message.size());
for (int i=0; i<(int)this->size(); i++) {
(*this)[i] = message[i];
}
return *this;
}
MidiEvent& MidiEvent::operator=(const vector<uchar>& bytes) {
clearVariables();
this->resize(bytes.size());
for (int i=0; i<(int)this->size(); i++) {
(*this)[i] = bytes[i];
}
return *this;
}
MidiEvent& MidiEvent::operator=(const vector<char>& bytes) {
clearVariables();
setMessage(bytes);
return *this;
}
MidiEvent& MidiEvent::operator=(const vector<int>& bytes) {
clearVariables();
setMessage(bytes);
return *this;
}
//////////////////////////////
//
// MidiEvent::unlinkEvent -- Disassociate this event with another.
// Also tell the other event to disassociate from this event.
//
void MidiEvent::unlinkEvent(void) {
if (m_eventlink == NULL) {
return;
}
MidiEvent* mev = m_eventlink;
m_eventlink = NULL;
mev->unlinkEvent();
}
//////////////////////////////
//
// MidiEvent::linkEvent -- Make a link between two messages.
// Unlinking
//
void MidiEvent::linkEvent(MidiEvent* mev) {
if (mev->m_eventlink != NULL) {
// unlink other event if it is linked to something else;
mev->unlinkEvent();
}
// if this is already linked to something else, then unlink:
if (m_eventlink != NULL) {
m_eventlink->unlinkEvent();
}
unlinkEvent();
mev->m_eventlink = this;
m_eventlink = mev;
}
void MidiEvent::linkEvent(MidiEvent& mev) {
linkEvent(&mev);
}
//////////////////////////////
//
// MidiEvent::getLinkedEvent -- Returns a linked event. Usually
// this is the note-off message for a note-on message and vice-versa.
// Returns null if there are no links.
//
MidiEvent* MidiEvent::getLinkedEvent(void) {
return m_eventlink;
}
const MidiEvent* MidiEvent::getLinkedEvent(void) const {
return m_eventlink;
}
//////////////////////////////
//
// MidiEvent::isLinked -- Returns true if there is an event which is not
// NULL. This function is similar to getLinkedEvent().
//
int MidiEvent::isLinked(void) const {
return m_eventlink == NULL ? 0 : 1;
}
//////////////////////////////
//
// MidiEvent::getTickDuration -- For linked events (note-ons and note-offs),
// return the absolute tick time difference between the two events.
// The tick values are presumed to be in absolute tick mode rather than
// delta tick mode. Returns 0 if not linked.
//
int MidiEvent::getTickDuration(void) const {
const MidiEvent* mev = getLinkedEvent();
if (mev == NULL) {
return 0;
}
int tick2 = mev->tick;
if (tick2 > tick) {
return tick2 - tick;
} else {
return tick - tick2;
}
}
//////////////////////////////
//
// MidiEvent::getDurationInSeconds -- For linked events (note-ons and
// note-offs), return the duration of the note in seconds. The
// seconds analysis must be done first; otherwise the duration will be
// reported as zero.
//
double MidiEvent::getDurationInSeconds(void) const {
const MidiEvent* mev = getLinkedEvent();
if (mev == NULL) {
return 0;
}
double seconds2 = mev->seconds;
if (seconds2 > seconds) {
return seconds2 - seconds;
} else {
return seconds - seconds2;
}
}
//////////////////////////////
//
// operator<<(MidiMessage) -- Print tick value followed by MIDI bytes for event.
// The tick value will be either relative or absolute depending on the state
// of the MidiFile object containing it.
//
std::ostream& operator<<(std::ostream& out, MidiEvent& event) {
out << event.tick << '(' << static_cast<MidiMessage&>(event) << ')';
return out;
}
} // end namespace smf

View File

@ -0,0 +1,78 @@
//
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Sat Feb 14 21:47:39 PST 2015
// Last Modified: Sat Apr 21 10:52:19 PDT 2018 Removed using namespace std;
// Filename: midifile/include/MidiEvent.h
// Website: http://midifile.sapp.org
// Syntax: C++11
// vim: ts=3 noexpandtab
//
// Description: A class which stores a MidiMessage and a timestamp
// for the MidiFile class.
//
#ifndef _MIDIEVENT_H_INCLUDED
#define _MIDIEVENT_H_INCLUDED
#include "MidiMessage.h"
#include <ostream>
#include <vector>
namespace smf {
class MidiEvent : public MidiMessage {
public:
MidiEvent (void);
MidiEvent (int command);
MidiEvent (int command, int param1);
MidiEvent (int command, int param1, int param2);
MidiEvent (const MidiMessage& message);
MidiEvent (const MidiEvent& mfevent);
MidiEvent (int aTime, int aTrack,
std::vector<uchar>& message);
~MidiEvent ();
MidiEvent& operator= (const MidiEvent& mfevent);
MidiEvent& operator= (const MidiMessage& message);
MidiEvent& operator= (const std::vector<uchar>& bytes);
MidiEvent& operator= (const std::vector<char>& bytes);
MidiEvent& operator= (const std::vector<int>& bytes);
void clearVariables (void);
// functions related to event linking (note-ons to note-offs).
void unlinkEvent (void);
void unlinkEvents (void);
void linkEvent (MidiEvent* mev);
void linkEvents (MidiEvent* mev);
void linkEvent (MidiEvent& mev);
void linkEvents (MidiEvent& mev);
int isLinked (void) const;
MidiEvent* getLinkedEvent (void);
const MidiEvent* getLinkedEvent (void) const;
int getTickDuration (void) const;
double getDurationInSeconds (void) const;
int tick; // delta or absolute MIDI ticks
int track; // [original] track number of event in MIDI file
double seconds; // calculated time in sec. (after doTimeAnalysis())
int seq; // sorting sequence number of event
private:
MidiEvent* m_eventlink; // used to match note-ons and note-offs
};
std::ostream& operator<<(std::ostream& out, MidiEvent& event);
} // end of namespace smf
#endif /* _MIDIEVENT_H_INCLUDED */

View File

@ -0,0 +1,623 @@
//
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Sat Feb 14 21:55:38 PST 2015
// Last Modified: Sat Apr 21 10:52:19 PDT 2018 Removed using namespace std;
// Filename: midifile/src/MidiEventList.cpp
// Website: http://midifile.sapp.org
// Syntax: C++11
// vim: ts=3 noexpandtab
//
// Description: A class which stores a MidiEvents for a MidiFile track.
//
#include "MidiEventList.h"
#include <algorithm>
#include <cstdlib>
#include <iterator>
#include <utility>
#include <vector>
namespace smf {
//////////////////////////////
//
// MidiEventList::MidiEventList -- Constructor.
//
MidiEventList::MidiEventList(void) {
reserve(1000);
}
//////////////////////////////
//
// MidiEventList::MidiEventList(MidiEventList&) -- Copy constructor.
//
MidiEventList::MidiEventList(const MidiEventList& other) {
list.reserve(other.list.size());
auto it = other.list.begin();
std::generate_n(std::back_inserter(list), other.list.size(), [&]() -> MidiEvent* {
return new MidiEvent(**it++);
});
}
//////////////////////////////
//
// MidiEventList::MidiEventList(MidiEventList&&) -- Move constructor.
//
MidiEventList::MidiEventList(MidiEventList&& other) {
list = std::move(other.list);
other.list.clear();
}
//////////////////////////////
//
// MidiEventList::~MidiEventList -- Deconstructor. Deallocate all stored
// data.
//
MidiEventList::~MidiEventList() {
clear();
}
//////////////////////////////
//
// MidiEventList::operator[] --
//
MidiEvent& MidiEventList::operator[](int index) {
return *list[index];
}
const MidiEvent& MidiEventList::operator[](int index) const {
return *list[index];
}
//////////////////////////////
//
// MidiEventList::back -- Return the last element in the list.
//
MidiEvent& MidiEventList::back(void) {
return *list.back();
}
const MidiEvent& MidiEventList::back(void) const {
return *list.back();
}
//
// MidiEventList::last -- Alias for MidiEventList::back().
//
MidiEvent& MidiEventList::last(void) {
return back();
}
const MidiEvent& MidiEventList::last(void) const {
return back();
}
//////////////////////////////
//
// MidiEventList::getEvent -- The same thing as operator[], for
// internal use when operator[] would look more messy.
//
MidiEvent& MidiEventList::getEvent(int index) {
return *list[index];
}
const MidiEvent& MidiEventList::getEvent(int index) const {
return *list[index];
}
//////////////////////////////
//
// MidiEventList::clear -- De-allocate any MidiEvents present in the list
// and set the size of the list to 0.
//
void MidiEventList::clear(void) {
for (auto& item : list) {
if (item != NULL) {
delete item;
item = NULL;
}
}
list.resize(0);
}
//////////////////////////////
//
// MidiEventList::data -- Return the low-level array of MidiMessage
// pointers. This is useful for applying your own sorting
// function to the list.
//
MidiEvent** MidiEventList::data(void) {
return list.data();
}
//////////////////////////////
//
// MidiEventList::reserve -- Pre-allocate space in the list for storing
// elements.
//
void MidiEventList::reserve(int rsize) {
if (rsize > (int)list.size()) {
list.reserve(rsize);
}
}
//////////////////////////////
//
// MidiEventList::getSize -- Return the number of MidiEvents stored
// in the list.
//
int MidiEventList::getSize(void) const {
return (int)list.size();
}
//
// MidiEventList::size -- Alias for MidiEventList::getSize().
//
int MidiEventList::size(void) const {
return getSize();
}
//
// MidiEventList::getEventCount -- Alias for MidiEventList::getSize().
//
int MidiEventList::getEventCount(void) const {
return getSize();
}
//////////////////////////////
//
// MidiEventList::append -- add a MidiEvent at the end of the list. Returns
// the index of the appended event.
//
int MidiEventList::append(MidiEvent& event) {
MidiEvent* ptr = new MidiEvent(event);
list.push_back(ptr);
return (int)list.size()-1;
}
//
// MidiEventList::push -- Alias for MidiEventList::append().
//
int MidiEventList::push(MidiEvent& event) {
return append(event);
}
//
// MidiEventList::push_back -- Alias for MidiEventList::append().
//
int MidiEventList::push_back(MidiEvent& event) {
return append(event);
}
//////////////////////////////
//
// MidiEventList::removeEmpties -- Remove any MIDI message which contain no
// bytes. This function first deallocates any empty MIDI events, and then
// removes them from the list of events.
//
void MidiEventList::removeEmpties(void) {
int count = 0;
for (auto& item : list) {
if (item->empty()) {
delete item;
item = NULL;
count++;
}
}
if (count == 0) {
return;
}
std::vector<MidiEvent*> newlist;
newlist.reserve(list.size() - count);
for (auto& item : list) {
if (item) {
newlist.push_back(item);
}
}
list.swap(newlist);
}
//////////////////////////////
//
// MidiEventList::linkNotePairs -- Match note-ones and note-offs together
// There are two models that can be done if two notes are overlapping
// on the same pitch: the first note-off affects the last note-on,
// or the first note-off affects the first note-on. Currently the
// first note-off affects the last note-on, but both methods could
// be implemented with user selectability. The current state of the
// track is assumed to be in time-sorted order. Returns the number
// of linked notes (note-on/note-off pairs).
//
int MidiEventList::linkEventPairs(void) {
return linkNotePairs();
}
int MidiEventList::linkNotePairs(void) {
// Note-on states:
// dimension 1: MIDI channel (0-15)
// dimension 2: MIDI key (0-127) (but 0 not used for note-ons)
// dimension 3: List of active note-ons or note-offs.
std::vector<std::vector<std::vector<MidiEvent*>>> noteons;
noteons.resize(16);
for (auto& noteon : noteons) {
noteon.resize(128);
}
// Controller linking: The following General MIDI controller numbers are
// also monitored for linking within the track (but not between tracks).
// hex dec name range
// 40 64 Hold pedal (Sustain) on/off 0..63=off 64..127=on
// 41 65 Portamento on/off 0..63=off 64..127=on
// 42 66 Sustenuto Pedal on/off 0..63=off 64..127=on
// 43 67 Soft Pedal on/off 0..63=off 64..127=on
// 44 68 Legato Pedal on/off 0..63=off 64..127=on
// 45 69 Hold Pedal 2 on/off 0..63=off 64..127=on
// 50 80 General Purpose Button 0..63=off 64..127=on
// 51 81 General Purpose Button 0..63=off 64..127=on
// 52 82 General Purpose Button 0..63=off 64..127=on
// 53 83 General Purpose Button 0..63=off 64..127=on
// 54 84 Undefined on/off 0..63=off 64..127=on
// 55 85 Undefined on/off 0..63=off 64..127=on
// 56 86 Undefined on/off 0..63=off 64..127=on
// 57 87 Undefined on/off 0..63=off 64..127=on
// 58 88 Undefined on/off 0..63=off 64..127=on
// 59 89 Undefined on/off 0..63=off 64..127=on
// 5A 90 Undefined on/off 0..63=off 64..127=on
// 7A 122 Local Keyboard On/Off 0..63=off 64..127=on
// first keep track of whether the controller is an on/off switch:
std::vector<std::pair<int, int>> contmap;
contmap.resize(128);
std::pair<int, int> zero(0, 0);
std::fill(contmap.begin(), contmap.end(), zero);
contmap[64].first = 1; contmap[64].second = 0;
contmap[65].first = 1; contmap[65].second = 1;
contmap[66].first = 1; contmap[66].second = 2;
contmap[67].first = 1; contmap[67].second = 3;
contmap[68].first = 1; contmap[68].second = 4;
contmap[69].first = 1; contmap[69].second = 5;
contmap[80].first = 1; contmap[80].second = 6;
contmap[81].first = 1; contmap[81].second = 7;
contmap[82].first = 1; contmap[82].second = 8;
contmap[83].first = 1; contmap[83].second = 9;
contmap[84].first = 1; contmap[84].second = 10;
contmap[85].first = 1; contmap[85].second = 11;
contmap[86].first = 1; contmap[86].second = 12;
contmap[87].first = 1; contmap[87].second = 13;
contmap[88].first = 1; contmap[88].second = 14;
contmap[89].first = 1; contmap[89].second = 15;
contmap[90].first = 1; contmap[90].second = 16;
contmap[122].first = 1; contmap[122].second = 17;
// dimensions:
// 1: mapped controller (0 to 17)
// 2: channel (0 to 15)
std::vector<std::vector<MidiEvent*>> contevents;
contevents.resize(18);
std::vector<std::vector<int>> oldstates;
oldstates.resize(18);
for (int i=0; i<18; i++) {
contevents[i].resize(16);
std::fill(contevents[i].begin(), contevents[i].end(), nullptr);
oldstates[i].resize(16);
std::fill(oldstates[i].begin(), oldstates[i].end(), -1);
}
// Now iterate through the MidiEventList keeping track of note and
// select controller states and linking notes/controllers as needed.
int channel;
int key;
int contnum;
int contval;
int conti;
int contstate;
int counter = 0;
MidiEvent* mev;
MidiEvent* noteon;
for (int i=0; i<getSize(); i++) {
mev = &getEvent(i);
mev->unlinkEvent();
if (mev->isNoteOn()) {
// store the note-on to pair later with a note-off message.
key = mev->getKeyNumber();
channel = mev->getChannel();
noteons[channel][key].push_back(mev);
} else if (mev->isNoteOff()) {
key = mev->getKeyNumber();
channel = mev->getChannel();
if (noteons[channel][key].size() > 0) {
noteon = noteons[channel][key].back();
noteons[channel][key].pop_back();
noteon->linkEvent(mev);
counter++;
}
} else if (mev->isController()) {
contnum = mev->getP1();
if (contmap[contnum].first) {
conti = contmap[contnum].second;
channel = mev->getChannel();
contval = mev->getP2();
contstate = contval < 64 ? 0 : 1;
if ((oldstates[conti][channel] == -1) && contstate) {
// a newly initialized onstate was detected, so store for
// later linking to an off state.
contevents[conti][channel] = mev;
oldstates[conti][channel] = contstate;
} else if (oldstates[conti][channel] == contstate) {
// the controller state is redundant and will be ignored.
} else if ((oldstates[conti][channel] == 0) && contstate) {
// controller is currently off, so store on-state for next link
contevents[conti][channel] = mev;
oldstates[conti][channel] = contstate;
} else if ((oldstates[conti][channel] == 1) && (contstate == 0)) {
// controller has just been turned off, so link to
// stored on-message.
contevents[conti][channel]->linkEvent(mev);
oldstates[conti][channel] = contstate;
// not necessary, but maybe use for something later:
contevents[conti][channel] = mev;
}
}
}
}
return counter;
}
//////////////////////////////
//
// MidiEventList::clearLinks -- remove all note-on/note-off links.
//
void MidiEventList::clearLinks(void) {
for (int i=0; i<(int)getSize(); i++) {
getEvent(i).unlinkEvent();
}
}
//////////////////////////////
//
// MidiEventList::clearSequence -- Remove any sequence serial numbers from
// MidiEvents in the list. This will cause the default ordering by
// sortTracks() to be used, in which case the ordering of MidiEvents
// occurring at the same tick may switch their ordering.
//
void MidiEventList::clearSequence(void) {
for (int i=0; i<getEventCount(); i++) {
getEvent(i).seq = 0;
}
}
//////////////////////////////
//
// MidiEventList::markSequence -- Assign a sequence serial number to
// every MidiEvent in the event list. This is useful if you want
// to preseve the order of MIDI messages in a track when they occur
// at the same tick time. Particularly for use with joinTracks()
// or sortTracks(). markSequence will be done automatically when
// a MIDI file is read, in case the ordering of events occurring at
// the same time is important. Use clearSequence() to use the
// default sorting behavior of sortTracks() when events occur at the
// same time. Returns the next serial number that has not yet been
// used.
// default value: sequence = 1.
//
int MidiEventList::markSequence(int sequence) {
for (int i=0; i<getEventCount(); i++) {
getEvent(i).seq = sequence++;
}
return sequence;
}
///////////////////////////////////////////////////////////////////////////
//
// protected functions --
//
//////////////////////////////
//
// MidiEventList::detach -- De-allocate any MidiEvents present in the list
// and set the size of the list to 0.
//
void MidiEventList::detach(void) {
list.resize(0);
}
//////////////////////////////
//
// MidiEventList::push_back_no_copy -- add a MidiEvent at the end of
// the list. The event is not copied, but memory from the
// remote location is used. Returns the index of the appended event.
//
int MidiEventList::push_back_no_copy(MidiEvent* event) {
list.push_back(event);
return (int)list.size()-1;
}
//////////////////////////////
//
// MidiEventList::operator=(MidiEventList) -- Assignment.
//
MidiEventList& MidiEventList::operator=(MidiEventList& other) {
list.swap(other.list);
return *this;
}
///////////////////////////////////////////////////////////////////////////
//
// private functions
//
//////////////////////////////
//
// MidiEventList::sort -- Private because the MidiFile class keeps
// track of delta versus absolute tick states of the MidiEventList,
// and sorting is only allowed in absolute tick state (The MidiEventList
// does not know about delta/absolute tick states of its contents).
//
void MidiEventList::sort(void) {
qsort(data(), getEventCount(), sizeof(MidiEvent*), eventcompare);
}
///////////////////////////////////////////////////////////////////////////
//
// external functions
//
//////////////////////////////
//
// eventcompare -- Event comparison function for sorting tracks.
//
// Sorting rules:
// (1) sort by (absolute) tick value; otherwise, if tick values are the same:
// (2) end-of-track meta message is always last.
// (3) other meta-messages come before regular MIDI messages.
// (4) note-offs come after all other regular MIDI messages except note-ons.
// (5) note-ons come after all other regular MIDI messages.
//
int eventcompare(const void* a, const void* b) {
MidiEvent& aevent = **((MidiEvent**)a);
MidiEvent& bevent = **((MidiEvent**)b);
if (aevent.tick > bevent.tick) {
// aevent occurs after bevent
return +1;
} else if (aevent.tick < bevent.tick) {
// aevent occurs before bevent
return -1;
} else if ((aevent.seq != 0) && (bevent.seq != 0) && (aevent.seq > bevent.seq)) {
// aevent sequencing state occurs after bevent
// see MidiEventList::markSequence()
return +1;
} else if ((aevent.seq != 0) && (bevent.seq != 0) && (aevent.seq < bevent.seq)) {
// aevent sequencing state occurs before bevent
// see MidiEventList::markSequence()
return -1;
} else if (aevent.getP0() == 0xff && aevent.getP1() == 0x2f) {
// end-of-track meta-message should always be last (but won't really
// matter since the writing function ignores all end-of-track messages
// and writes its own.
return +1;
} else if (bevent.getP0() == 0xff && bevent.getP1() == 0x2f) {
// end-of-track meta-message should always be last (but won't really
// matter since the writing function ignores all end-of-track messages
// and writes its own.
return -1;
} else if (aevent.getP0() == 0xff && bevent.getP0() != 0xff) {
// other meta-messages are placed before real MIDI messages
return -1;
} else if (aevent.getP0() != 0xff && bevent.getP0() == 0xff) {
// other meta-messages are placed before real MIDI messages
return +1;
} else if (((aevent.getP0() & 0xf0) == 0x90) && (aevent.getP2() != 0)) {
// note-ons come after all other types of MIDI messages
return +1;
} else if (((bevent.getP0() & 0xf0) == 0x90) && (bevent.getP2() != 0)) {
// note-ons come after all other types of MIDI messages
return -1;
} else if (((aevent.getP0() & 0xf0) == 0x90) || ((aevent.getP0() & 0xf0) == 0x80)) {
// note-offs come after all other MIDI messages (except note-ons)
return +1;
} else if (((bevent.getP0() & 0xf0) == 0x90) || ((bevent.getP0() & 0xf0) == 0x80)) {
// note-offs come after all other MIDI messages (except note-ons)
return -1;
} else if (((aevent.getP0() & 0xf0) == 0xb0) && ((bevent.getP0() & 0xf0) == 0xb0)) {
// both events are continuous controllers. Sort them by controller number
if (aevent.getP1() > bevent.getP1()) {
return +1;
} if (aevent.getP1() < bevent.getP1()) {
return -1;
} else {
// same controller number, so sort by data value
if (aevent.getP2() > bevent.getP2()) {
return +1;
} if (aevent.getP2() < bevent.getP2()) {
return -1;
} else {
return 0;
}
}
} else {
return 0;
}
}
} // end namespace smf

View File

@ -0,0 +1,82 @@
//
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Sat Feb 14 21:55:38 PST 2015
// Last Modified: Sat Apr 21 10:52:19 PDT 2018 Removed using namespace std;
// Filename: midifile/include/MidiEventList.h
// Website: http://midifile.sapp.org
// Syntax: C++11
// vim: ts=3 noexpandtab
//
// Description: A class that stores a MidiEvents for a MidiFile track.
//
#ifndef _MIDIEVENTLIST_H_INCLUDED
#define _MIDIEVENTLIST_H_INCLUDED
#include "MidiEvent.h"
#include <vector>
namespace smf {
class MidiEventList {
public:
MidiEventList (void);
MidiEventList (const MidiEventList& other);
MidiEventList (MidiEventList&& other);
~MidiEventList ();
MidiEventList& operator= (MidiEventList& other);
MidiEvent& operator[] (int index);
const MidiEvent& operator[] (int index) const;
MidiEvent& back (void);
const MidiEvent& back (void) const;
MidiEvent& last (void);
const MidiEvent& last (void) const;
MidiEvent& getEvent (int index);
const MidiEvent& getEvent (int index) const;
void clear (void);
void reserve (int rsize);
int getEventCount (void) const;
int getSize (void) const;
int size (void) const;
void removeEmpties (void);
int linkNotePairs (void);
int linkEventPairs (void);
void clearLinks (void);
void clearSequence (void);
int markSequence (int sequence = 1);
int push (MidiEvent& event);
int push_back (MidiEvent& event);
int append (MidiEvent& event);
// careful when using these, intended for internal use in MidiFile class:
void detach (void);
int push_back_no_copy (MidiEvent* event);
// access to the list of MidiEvents for sorting with an external function:
MidiEvent** data (void);
protected:
std::vector<MidiEvent*> list;
private:
void sort (void);
// MidiFile class calls sort()
friend class MidiFile;
};
int eventcompare(const void* a, const void* b);
} // end of namespace smf
#endif /* _MIDIEVENTLIST_H_INCLUDED */

3465
src/3rdparty/midifile/MidiFile.cpp vendored 100644

File diff suppressed because it is too large Load Diff

332
src/3rdparty/midifile/MidiFile.h vendored 100644
View File

@ -0,0 +1,332 @@
//
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Fri Nov 26 14:12:01 PST 1999
// Last Modified: Mon Jan 18 20:54:04 PST 2021 Added readSmf().
// Filename: midifile/include/MidiFile.h
// Website: http://midifile.sapp.org
// Syntax: C++11
// vim: ts=3 noexpandtab
//
// Description: A class that can read/write Standard MIDI files.
// MIDI data is stored by track in an array.
//
#ifndef _MIDIFILE_H_INCLUDED
#define _MIDIFILE_H_INCLUDED
#include "MidiEventList.h"
#include <fstream>
#include <istream>
#include <string>
#include <vector>
namespace smf {
enum {
TRACK_STATE_SPLIT = 0, // Tracks are separated into separate vector postions.
TRACK_STATE_JOINED = 1 // Tracks are merged into a single vector position,
}; // like a Type-0 MIDI file, but reversible.
enum {
TIME_STATE_DELTA = 0, // MidiMessage::ticks are in delta time format (like MIDI file).
TIME_STATE_ABSOLUTE = 1 // MidiMessage::ticks are in absolute time format (0=start time).
};
class _TickTime {
public:
int tick;
double seconds;
};
class MidiFile {
public:
MidiFile (void);
MidiFile (const std::string& filename);
MidiFile (std::istream& input);
MidiFile (const MidiFile& other);
MidiFile (MidiFile&& other);
~MidiFile ();
MidiFile& operator= (const MidiFile& other);
MidiFile& operator= (MidiFile&& other);
// Reading/writing functions:
// Auto-detected SMF or ASCII-encoded SMF (decoded with Binasc class):
bool read (const std::string& filename);
bool read (std::istream& instream);
bool readBase64 (const std::string& base64data);
bool readBase64 (std::istream& instream);
// Only allow Standard MIDI File input:
bool readSmf (const std::string& filename);
bool readSmf (std::istream& instream);
bool write (const std::string& filename);
bool write (std::ostream& out);
bool writeBase64 (const std::string& out, int width = 0);
bool writeBase64 (std::ostream& out, int width = 0);
std::string getBase64 (int width = 0);
bool writeHex (const std::string& filename, int width = 25);
bool writeHex (std::ostream& out, int width = 25);
bool writeBinasc (const std::string& filename);
bool writeBinasc (std::ostream& out);
bool writeBinascWithComments (const std::string& filename);
bool writeBinascWithComments (std::ostream& out);
bool status (void) const;
// track-related functions:
const MidiEventList& operator[] (int aTrack) const;
MidiEventList& operator[] (int aTrack);
int getTrackCount (void) const;
int getNumTracks (void) const;
int size (void) const;
void removeEmpties (void);
// tick-related functions:
void makeDeltaTicks (void);
void deltaTicks (void);
void makeAbsoluteTicks (void);
void absoluteTicks (void);
int getTickState (void) const;
bool isDeltaTicks (void) const;
bool isAbsoluteTicks (void) const;
// join/split track functionality:
void joinTracks (void);
void splitTracks (void);
void splitTracksByChannel (void);
int getTrackState (void) const;
int hasJoinedTracks (void) const;
int hasSplitTracks (void) const;
int getSplitTrack (int track, int index) const;
int getSplitTrack (int index) const;
// track sorting funcionality:
void sortTrack (int track);
void sortTracks (void);
void markSequence (void);
void markSequence (int track, int sequence = 1);
void clearSequence (void);
void clearSequence (int track);
// track manipulation functionality:
int addTrack (void);
int addTrack (int count);
int addTracks (int count);
void deleteTrack (int aTrack);
void mergeTracks (int aTrack1, int aTrack2);
int getTrackCountAsType1 (void);
// ticks-per-quarter related functions:
void setMillisecondTicks (void);
int getTicksPerQuarterNote (void) const;
int getTPQ (void) const;
void setTicksPerQuarterNote (int ticks);
void setTPQ (int ticks);
// physical-time analysis functions:
void doTimeAnalysis (void);
double getTimeInSeconds (int aTrack, int anIndex);
double getTimeInSeconds (int tickvalue);
double getAbsoluteTickTime (double starttime);
int getFileDurationInTicks (void);
double getFileDurationInQuarters (void);
double getFileDurationInSeconds (void);
// note-analysis functions:
int linkNotePairs (void);
int linkEventPairs (void);
void clearLinks (void);
// filename functions:
void setFilename (const std::string& aname);
const char* getFilename (void) const;
// event functionality:
MidiEvent* addEvent (int aTrack, int aTick,
std::vector<uchar>& midiData);
MidiEvent* addEvent (MidiEvent& mfevent);
MidiEvent* addEvent (int aTrack, MidiEvent& mfevent);
MidiEvent& getEvent (int aTrack, int anIndex);
const MidiEvent& getEvent (int aTrack, int anIndex) const;
int getEventCount (int aTrack) const;
int getNumEvents (int aTrack) const;
void allocateEvents (int track, int aSize);
void erase (void);
void clear (void);
void clear_no_deallocate (void);
// MIDI message adding convenience functions:
MidiEvent* addNoteOn (int aTrack, int aTick,
int aChannel, int key,
int vel);
MidiEvent* addNoteOff (int aTrack, int aTick,
int aChannel, int key,
int vel);
MidiEvent* addNoteOff (int aTrack, int aTick,
int aChannel, int key);
MidiEvent* addController (int aTrack, int aTick,
int aChannel, int num,
int value);
MidiEvent* addPatchChange (int aTrack, int aTick,
int aChannel, int patchnum);
MidiEvent* addTimbre (int aTrack, int aTick,
int aChannel, int patchnum);
MidiEvent* addPitchBend (int aTrack, int aTick,
int aChannel, double amount);
// RPN settings:
void setPitchBendRange (int aTrack, int aTick,
int aChannel, double range);
// Controller message adding convenience functions:
MidiEvent* addSustain (int aTrack, int aTick,
int aChannel, int value);
MidiEvent* addSustainPedal (int aTrack, int aTick,
int aChannel, int value);
MidiEvent* addSustainOn (int aTrack, int aTick,
int aChannel);
MidiEvent* addSustainPedalOn (int aTrack, int aTick,
int aChannel);
MidiEvent* addSustainOff (int aTrack, int aTick,
int aChannel);
MidiEvent* addSustainPedalOff (int aTrack, int aTick,
int aChannel);
// Meta-event adding convenience functions:
MidiEvent* addMetaEvent (int aTrack, int aTick,
int aType,
std::vector<uchar>& metaData);
MidiEvent* addMetaEvent (int aTrack, int aTick,
int aType,
const std::string& metaData);
MidiEvent* addText (int aTrack, int aTick,
const std::string& text);
MidiEvent* addCopyright (int aTrack, int aTick,
const std::string& text);
MidiEvent* addTrackName (int aTrack, int aTick,
const std::string& name);
MidiEvent* addInstrumentName (int aTrack, int aTick,
const std::string& name);
MidiEvent* addLyric (int aTrack, int aTick,
const std::string& text);
MidiEvent* addMarker (int aTrack, int aTick,
const std::string& text);
MidiEvent* addCue (int aTrack, int aTick,
const std::string& text);
MidiEvent* addTempo (int aTrack, int aTick,
double aTempo);
MidiEvent* addKeySignature (int aTrack, int aTick,
int fifths, bool mode = 0);
MidiEvent* addTimeSignature (int aTrack, int aTick,
int top, int bottom,
int clocksPerClick = 24,
int num32dsPerQuarter = 8);
MidiEvent* addCompoundTimeSignature(int aTrack, int aTick,
int top, int bottom,
int clocksPerClick = 36,
int num32dsPerQuarter = 8);
uchar readByte (std::istream& input);
// static functions:
static ushort readLittleEndian2Bytes (std::istream& input);
static ulong readLittleEndian4Bytes (std::istream& input);
static std::ostream& writeLittleEndianUShort (std::ostream& out,
ushort value);
static std::ostream& writeBigEndianUShort (std::ostream& out,
ushort value);
static std::ostream& writeLittleEndianShort (std::ostream& out,
short value);
static std::ostream& writeBigEndianShort (std::ostream& out,
short value);
static std::ostream& writeLittleEndianULong (std::ostream& out,
ulong value);
static std::ostream& writeBigEndianULong (std::ostream& out,
ulong value);
static std::ostream& writeLittleEndianLong (std::ostream& out,
long value);
static std::ostream& writeBigEndianLong (std::ostream& out,
long value);
static std::ostream& writeLittleEndianFloat (std::ostream& out,
float value);
static std::ostream& writeBigEndianFloat (std::ostream& out,
float value);
static std::ostream& writeLittleEndianDouble (std::ostream& out,
double value);
static std::ostream& writeBigEndianDouble (std::ostream& out,
double value);
static std::string getGMInstrumentName (int patchIndex);
protected:
// m_events == Lists of MidiEvents for each MIDI file track.
std::vector<MidiEventList*> m_events;
// m_ticksPerQuarterNote == A value for the MIDI file header
// which represents the number of ticks in a quarter note
// that are used as units for the delta times for MIDI events
// in MIDI file track data.
int m_ticksPerQuarterNote = 120;
// m_theTrackState == state variable for whether the tracks
// are joined or split.
int m_theTrackState = TRACK_STATE_SPLIT;
// m_theTimeState == state variable for whether the MidiEvent::tick
// variable contain absolute ticks since the start of the file's
// time, or delta ticks since the last MIDI event in the track.
int m_theTimeState = TIME_STATE_ABSOLUTE;
// m_readFileName == the filename of the last file read into
// the object.
std::string m_readFileName;
// m_timemapvalid ==
bool m_timemapvalid = false;
// m_timemap ==
std::vector<_TickTime> m_timemap;
// m_rwstatus == True if last read was successful, false if a problem.
bool m_rwstatus = true;
// m_linkedEventQ == True if link analysis has been done.
bool m_linkedEventsQ = false;
private:
int extractMidiData (std::istream& inputfile,
std::vector<uchar>& array,
uchar& runningCommand);
ulong readVLValue (std::istream& inputfile);
ulong unpackVLV (uchar a = 0, uchar b = 0,
uchar c = 0, uchar d = 0,
uchar e = 0);
void writeVLValue (long aValue,
std::vector<uchar>& data);
int makeVLV (uchar *buffer, int number);
static int ticksearch (const void* A, const void* B);
static int secondsearch (const void* A, const void* B);
void buildTimeMap (void);
double linearTickInterpolationAtSecond (double seconds);
double linearSecondInterpolationAtTick (int ticktime);
std::string base64Encode (const std::string &input);
std::string base64Decode (const std::string &input);
static const std::string encodeLookup;
static const std::vector<int> decodeLookup;
static const char *GMinstrument[128];
};
} // end of namespace smf
std::ostream& operator<<(std::ostream& out, smf::MidiFile& aMidiFile);
#endif /* _MIDIFILE_H_INCLUDED */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,219 @@
//
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Sat Feb 14 20:36:32 PST 2015
// Last Modified: Sat Apr 21 10:52:19 PDT 2018 Removed using namespace std;
// Filename: midifile/include/MidiMessage.h
// Website: http://midifile.sapp.org
// Syntax: C++11
// vim: ts=3 noexpandtab
//
// Description: Storage for bytes of a MIDI message for use in MidiFile
// class.
//
#ifndef _MIDIMESSAGE_H_INCLUDED
#define _MIDIMESSAGE_H_INCLUDED
#include <iostream>
#include <string>
#include <utility>
#include <vector>
namespace smf {
typedef unsigned char uchar;
typedef unsigned short ushort;
typedef unsigned long ulong;
class MidiMessage : public std::vector<uchar> {
public:
MidiMessage (void);
MidiMessage (int command);
MidiMessage (int command, int p1);
MidiMessage (int command, int p1, int p2);
MidiMessage (const MidiMessage& message);
MidiMessage (const std::vector<uchar>& message);
MidiMessage (const std::vector<char>& message);
MidiMessage (const std::vector<int>& message);
~MidiMessage ();
MidiMessage& operator= (const MidiMessage& message);
MidiMessage& operator= (const std::vector<uchar>& bytes);
MidiMessage& operator= (const std::vector<char>& bytes);
MidiMessage& operator= (const std::vector<int>& bytes);
void sortTrack (void);
void sortTrackWithSequence(void);
static std::vector<uchar> intToVlv (int value);
static double frequencyToSemitones (double frequency, double a4frequency = 440.0);
// data access convenience functions (returns -1 if not present):
int getP0 (void) const;
int getP1 (void) const;
int getP2 (void) const;
int getP3 (void) const;
void setP0 (int value);
void setP1 (int value);
void setP2 (int value);
void setP3 (int value);
int getSize (void) const;
void setSize (int asize);
int setSizeToCommand (void);
int resizeToCommand (void);
// note-message convenience functions:
int getKeyNumber (void) const;
int getVelocity (void) const;
void setKeyNumber (int value);
void setVelocity (int value);
void setSpelling (int base7, int accidental);
void getSpelling (int& base7, int& accidental);
// controller-message convenience functions:
int getControllerNumber (void) const;
int getControllerValue (void) const;
int getCommandNibble (void) const;
int getCommandByte (void) const;
int getChannelNibble (void) const;
int getChannel (void) const;
void setCommandByte (int value);
void setCommand (int value);
void setCommand (int value, int p1);
void setCommand (int value, int p1, int p2);
void setCommandNibble (int value);
void setChannelNibble (int value);
void setChannel (int value);
void setParameters (int p1, int p2);
void setParameters (int p1);
void setMessage (const std::vector<uchar>& message);
void setMessage (const std::vector<char>& message);
void setMessage (const std::vector<int>& message);
// message-type convenience functions:
bool isMetaMessage (void) const;
bool isMeta (void) const;
bool isNote (void) const;
bool isNoteOff (void) const;
bool isNoteOn (void) const;
bool isAftertouch (void) const;
bool isController (void) const;
bool isSustain (void) const; // controller 64
bool isSustainOn (void) const;
bool isSustainOff (void) const;
bool isSoft (void) const; // controller 67
bool isSoftOn (void) const;
bool isSoftOff (void) const;
bool isPatchChange (void) const;
bool isTimbre (void) const;
bool isPressure (void) const;
bool isPitchbend (void) const;
bool isEmpty (void) const; // see MidiFile::removeEmpties()
// helper functions to create various MidiMessages:
void makeNoteOn (int channel, int key, int velocity);
void makeNoteOff (int channel, int key, int velocity);
void makeNoteOff (int channel, int key);
void makeNoteOff (void);
void makePatchChange (int channel, int patchnum);
void makeTimbre (int channel, int patchnum);
void makeController (int channel, int num, int value);
void makePitchBend (int channel, int lsb, int msb);
void makePitchBend (int channel, int value);
void makePitchBendDouble (int channel, double value);
void makePitchbend (int channel, int lsb, int msb) { makePitchBend(channel, lsb, msb); }
void makePitchbend (int channel, int value) { makePitchBend(channel, value); }
void makePitchbendDouble (int channel, double value) { makePitchBendDouble(channel, value); }
// helper functions to create various continuous controller messages:
void makeSustain (int channel, int value);
void makeSustainPedal (int channel, int value);
void makeSustainOn (int channel);
void makeSustainPedalOn (int channel);
void makeSustainOff (int channel);
void makeSustainPedalOff (int channel);
// meta-message creation and helper functions:
void makeMetaMessage (int mnum, const std::string& data);
void makeText (const std::string& name);
void makeCopyright (const std::string& text);
void makeTrackName (const std::string& name);
void makeInstrumentName (const std::string& name);
void makeLyric (const std::string& text);
void makeMarker (const std::string& text);
void makeCue (const std::string& text);
void makeKeySignature (int fifths, bool mode = 0);
void makeTimeSignature (int top, int bottom,
int clocksPerClick = 24,
int num32dsPerQuarter = 8);
void makeTempo (double tempo) { setTempo(tempo); }
int getTempoMicro (void) const;
int getTempoMicroseconds (void) const;
double getTempoSeconds (void) const;
double getTempoBPM (void) const;
double getTempoTPS (int tpq) const;
double getTempoSPT (int tpq) const;
int getMetaType (void) const;
bool isText (void) const;
bool isCopyright (void) const;
bool isTrackName (void) const;
bool isInstrumentName (void) const;
bool isLyricText (void) const;
bool isMarkerText (void) const;
bool isTempo (void) const;
bool isTimeSignature (void) const;
bool isKeySignature (void) const;
bool isEndOfTrack (void) const;
std::string getMetaContent (void) const;
void setMetaContent (const std::string& content);
void setTempo (double tempo);
void setTempoMicroseconds (int microseconds);
void setMetaTempo (double tempo);
void makeSysExMessage (const std::vector<uchar>& data);
// helper functions to create MTS tunings by key (real-time sysex)
// MTS type 2: Real-time frequency assignment to a arbitrary list of MIDI key numbers.
// See page 2 of: https://docs.google.com/viewer?url=https://www.midi.org/component/edocman/midi-tuning-updated/fdocument?Itemid=9999
void makeMts2_KeyTuningByFrequency (int key, double frequency, int program = 0);
void makeMts2_KeyTuningsByFrequency (int key, double frequency, int program = 0);
void makeMts2_KeyTuningsByFrequency (std::vector<std::pair<int, double>>& mapping, int program = 0);
void makeMts2_KeyTuningBySemitone (int key, double semitone, int program = 0);
void makeMts2_KeyTuningsBySemitone (int key, double semitone, int program = 0);
void makeMts2_KeyTuningsBySemitone (std::vector<std::pair<int, double>>& mapping, int program = 0);
// MTS type 9: Real-time octave temperaments by +/- 100 cents deviation from ET
// See page 7 of: https://docs.google.com/viewer?url=https://www.midi.org/component/edocman/midi-tuning-updated/fdocument?Itemid=9999
void makeMts9_TemperamentByCentsDeviationFromET (std::vector<double>& mapping, int referencePitchClass = 0, int channelMask = 0b1111111111111111);
void makeTemperamentEqual(int referencePitchClass = 0, int channelMask = 0b1111111111111111);
void makeTemperamentBad(double maxDeviationCents = 100.0, int referencePitchClass = 0, int channelMask = 0b1111111111111111);
void makeTemperamentPythagorean(int referencePitchClass = 2, int channelMask = 0b1111111111111111);
void makeTemperamentMeantone(double fraction = 0.25, int referencePitchClass = 2, int channelMask = 0b1111111111111111);
void makeTemperamentMeantoneCommaQuarter(int referencePitchClass = 2, int channelMask = 0b1111111111111111);
void makeTemperamentMeantoneCommaThird(int referencePitchClass = 2, int channelMask = 0b1111111111111111);
void makeTemperamentMeantoneCommaHalf(int referencePitchClass = 2, int channelMask = 0b1111111111111111);
};
std::ostream& operator<<(std::ostream& out, MidiMessage& event);
} // end of namespace smf
#endif /* _MIDIMESSAGE_H_INCLUDED */

View File

@ -321,19 +321,19 @@ void Blitter_32bppAnim::DrawColourMappingRect(void *dst, int width, int height,
Debug(misc, 0, "32bpp blitter doesn't know how to draw this colour table ('{}')", pal); Debug(misc, 0, "32bpp blitter doesn't know how to draw this colour table ('{}')", pal);
} }
void Blitter_32bppAnim::SetPixel(void *video, int x, int y, uint8_t colour) void Blitter_32bppAnim::SetPixel(void *video, int x, int y, PixelColour colour)
{ {
*((Colour *)video + x + y * _screen.pitch) = LookupColourInPalette(colour); *((Colour *)video + x + y * _screen.pitch) = LookupColourInPalette(colour.p);
/* Set the colour in the anim-buffer too, if we are rendering to the screen */ /* Set the colour in the anim-buffer too, if we are rendering to the screen */
if (_screen_disable_anim) return; if (_screen_disable_anim) return;
this->anim_buf[this->ScreenToAnimOffset((uint32_t *)video) + x + y * this->anim_buf_pitch] = colour | (DEFAULT_BRIGHTNESS << 8); this->anim_buf[this->ScreenToAnimOffset((uint32_t *)video) + x + y * this->anim_buf_pitch] = colour.p | (DEFAULT_BRIGHTNESS << 8);
} }
void Blitter_32bppAnim::DrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, uint8_t colour, int width, int dash) void Blitter_32bppAnim::DrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, PixelColour colour, int width, int dash)
{ {
const Colour c = LookupColourInPalette(colour); const Colour c = LookupColourInPalette(colour.p);
if (_screen_disable_anim) { if (_screen_disable_anim) {
this->DrawLineGeneric(x, y, x2, y2, screen_width, screen_height, width, dash, [&](int x, int y) { this->DrawLineGeneric(x, y, x2, y2, screen_width, screen_height, width, dash, [&](int x, int y) {
@ -341,7 +341,7 @@ void Blitter_32bppAnim::DrawLine(void *video, int x, int y, int x2, int y2, int
}); });
} else { } else {
uint16_t * const offset_anim_buf = this->anim_buf + this->ScreenToAnimOffset((uint32_t *)video); uint16_t * const offset_anim_buf = this->anim_buf + this->ScreenToAnimOffset((uint32_t *)video);
const uint16_t anim_colour = colour | (DEFAULT_BRIGHTNESS << 8); const uint16_t anim_colour = colour.p | (DEFAULT_BRIGHTNESS << 8);
this->DrawLineGeneric(x, y, x2, y2, screen_width, screen_height, width, dash, [&](int x, int y) { this->DrawLineGeneric(x, y, x2, y2, screen_width, screen_height, width, dash, [&](int x, int y) {
*((Colour *)video + x + y * _screen.pitch) = c; *((Colour *)video + x + y * _screen.pitch) = c;
offset_anim_buf[x + y * this->anim_buf_pitch] = anim_colour; offset_anim_buf[x + y * this->anim_buf_pitch] = anim_colour;
@ -349,7 +349,7 @@ void Blitter_32bppAnim::DrawLine(void *video, int x, int y, int x2, int y2, int
} }
} }
void Blitter_32bppAnim::DrawRect(void *video, int width, int height, uint8_t colour) void Blitter_32bppAnim::DrawRect(void *video, int width, int height, PixelColour colour)
{ {
if (_screen_disable_anim) { if (_screen_disable_anim) {
/* This means our output is not to the screen, so we can't be doing any animation stuff, so use our parent DrawRect() */ /* This means our output is not to the screen, so we can't be doing any animation stuff, so use our parent DrawRect() */
@ -357,7 +357,7 @@ void Blitter_32bppAnim::DrawRect(void *video, int width, int height, uint8_t col
return; return;
} }
Colour colour32 = LookupColourInPalette(colour); Colour colour32 = LookupColourInPalette(colour.p);
uint16_t *anim_line = this->ScreenToAnimOffset((uint32_t *)video) + this->anim_buf; uint16_t *anim_line = this->ScreenToAnimOffset((uint32_t *)video) + this->anim_buf;
do { do {
@ -367,7 +367,7 @@ void Blitter_32bppAnim::DrawRect(void *video, int width, int height, uint8_t col
for (int i = width; i > 0; i--) { for (int i = width; i > 0; i--) {
*dst = colour32; *dst = colour32;
/* Set the colour in the anim-buffer too */ /* Set the colour in the anim-buffer too */
*anim = colour | (DEFAULT_BRIGHTNESS << 8); *anim = colour.p | (DEFAULT_BRIGHTNESS << 8);
dst++; dst++;
anim++; anim++;
} }

View File

@ -34,9 +34,9 @@ public:
void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) override; void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) override;
void DrawColourMappingRect(void *dst, int width, int height, PaletteID pal) override; void DrawColourMappingRect(void *dst, int width, int height, PaletteID pal) override;
void SetPixel(void *video, int x, int y, uint8_t colour) override; void SetPixel(void *video, int x, int y, PixelColour colour) override;
void DrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, uint8_t colour, int width, int dash) override; void DrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, PixelColour colour, int width, int dash) override;
void DrawRect(void *video, int width, int height, uint8_t colour) override; void DrawRect(void *video, int width, int height, PixelColour colour) override;
void CopyFromBuffer(void *video, const void *src, int width, int height) override; void CopyFromBuffer(void *video, const void *src, int width, int height) override;
void CopyToBuffer(const void *video, void *dst, int width, int height) override; void CopyToBuffer(const void *video, void *dst, int width, int height) override;
void ScrollBuffer(void *video, int &left, int &top, int &width, int &height, int scroll_x, int scroll_y) override; void ScrollBuffer(void *video, int &left, int &top, int &width, int &height, int scroll_x, int scroll_y) override;

View File

@ -18,22 +18,22 @@ void *Blitter_32bppBase::MoveTo(void *video, int x, int y)
return (uint32_t *)video + x + y * _screen.pitch; return (uint32_t *)video + x + y * _screen.pitch;
} }
void Blitter_32bppBase::SetPixel(void *video, int x, int y, uint8_t colour) void Blitter_32bppBase::SetPixel(void *video, int x, int y, PixelColour colour)
{ {
*((Colour *)video + x + y * _screen.pitch) = LookupColourInPalette(colour); *((Colour *)video + x + y * _screen.pitch) = LookupColourInPalette(colour.p);
} }
void Blitter_32bppBase::DrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, uint8_t colour, int width, int dash) void Blitter_32bppBase::DrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, PixelColour colour, int width, int dash)
{ {
const Colour c = LookupColourInPalette(colour); const Colour c = LookupColourInPalette(colour.p);
this->DrawLineGeneric(x, y, x2, y2, screen_width, screen_height, width, dash, [=](int x, int y) { this->DrawLineGeneric(x, y, x2, y2, screen_width, screen_height, width, dash, [=](int x, int y) {
*((Colour *)video + x + y * _screen.pitch) = c; *((Colour *)video + x + y * _screen.pitch) = c;
}); });
} }
void Blitter_32bppBase::DrawRect(void *video, int width, int height, uint8_t colour) void Blitter_32bppBase::DrawRect(void *video, int width, int height, PixelColour colour)
{ {
Colour colour32 = LookupColourInPalette(colour); Colour colour32 = LookupColourInPalette(colour.p);
do { do {
Colour *dst = (Colour *)video; Colour *dst = (Colour *)video;

View File

@ -19,9 +19,9 @@ class Blitter_32bppBase : public Blitter {
public: public:
uint8_t GetScreenDepth() override { return 32; } uint8_t GetScreenDepth() override { return 32; }
void *MoveTo(void *video, int x, int y) override; void *MoveTo(void *video, int x, int y) override;
void SetPixel(void *video, int x, int y, uint8_t colour) override; void SetPixel(void *video, int x, int y, PixelColour colour) override;
void DrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, uint8_t colour, int width, int dash) override; void DrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, PixelColour colour, int width, int dash) override;
void DrawRect(void *video, int width, int height, uint8_t colour) override; void DrawRect(void *video, int width, int height, PixelColour colour) override;
void CopyFromBuffer(void *video, const void *src, int width, int height) override; void CopyFromBuffer(void *video, const void *src, int width, int height) override;
void CopyToBuffer(const void *video, void *dst, int width, int height) override; void CopyToBuffer(const void *video, void *dst, int width, int height) override;
void CopyImageToBuffer(const void *video, void *dst, int width, int height, int dst_pitch) override; void CopyImageToBuffer(const void *video, void *dst, int width, int height, int dst_pitch) override;

View File

@ -27,7 +27,7 @@ static FBlitter_40bppAnim iFBlitter_40bppAnim;
static const Colour _black_colour(0, 0, 0); static const Colour _black_colour(0, 0, 0);
void Blitter_40bppAnim::SetPixel(void *video, int x, int y, uint8_t colour) void Blitter_40bppAnim::SetPixel(void *video, int x, int y, PixelColour colour)
{ {
if (_screen_disable_anim) { if (_screen_disable_anim) {
Blitter_32bppOptimized::SetPixel(video, x, y, colour); Blitter_32bppOptimized::SetPixel(video, x, y, colour);
@ -35,11 +35,11 @@ void Blitter_40bppAnim::SetPixel(void *video, int x, int y, uint8_t colour)
size_t y_offset = static_cast<size_t>(y) * _screen.pitch; size_t y_offset = static_cast<size_t>(y) * _screen.pitch;
*((Colour *)video + x + y_offset) = _black_colour; *((Colour *)video + x + y_offset) = _black_colour;
VideoDriver::GetInstance()->GetAnimBuffer()[((uint32_t *)video - (uint32_t *)_screen.dst_ptr) + x + y_offset] = colour; VideoDriver::GetInstance()->GetAnimBuffer()[((uint32_t *)video - (uint32_t *)_screen.dst_ptr) + x + y_offset] = colour.p;
} }
} }
void Blitter_40bppAnim::DrawRect(void *video, int width, int height, uint8_t colour) void Blitter_40bppAnim::DrawRect(void *video, int width, int height, PixelColour colour)
{ {
if (_screen_disable_anim) { if (_screen_disable_anim) {
/* This means our output is not to the screen, so we can't be doing any animation stuff, so use our parent DrawRect() */ /* This means our output is not to the screen, so we can't be doing any animation stuff, so use our parent DrawRect() */
@ -56,7 +56,7 @@ void Blitter_40bppAnim::DrawRect(void *video, int width, int height, uint8_t col
for (int i = width; i > 0; i--) { for (int i = width; i > 0; i--) {
*dst = _black_colour; *dst = _black_colour;
*anim = colour; *anim = colour.p;
dst++; dst++;
anim++; anim++;
} }
@ -65,7 +65,7 @@ void Blitter_40bppAnim::DrawRect(void *video, int width, int height, uint8_t col
} while (--height); } while (--height);
} }
void Blitter_40bppAnim::DrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, uint8_t colour, int width, int dash) void Blitter_40bppAnim::DrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, PixelColour colour, int width, int dash)
{ {
if (_screen_disable_anim) { if (_screen_disable_anim) {
/* This means our output is not to the screen, so we can't be doing any animation stuff, so use our parent DrawRect() */ /* This means our output is not to the screen, so we can't be doing any animation stuff, so use our parent DrawRect() */
@ -78,7 +78,7 @@ void Blitter_40bppAnim::DrawLine(void *video, int x, int y, int x2, int y2, int
this->DrawLineGeneric(x, y, x2, y2, screen_width, screen_height, width, dash, [=](int x, int y) { this->DrawLineGeneric(x, y, x2, y2, screen_width, screen_height, width, dash, [=](int x, int y) {
*((Colour *)video + x + y * _screen.pitch) = _black_colour; *((Colour *)video + x + y * _screen.pitch) = _black_colour;
*(anim + x + y * _screen.pitch) = colour; *(anim + x + y * _screen.pitch) = colour.p;
}); });
} }

View File

@ -18,9 +18,9 @@
class Blitter_40bppAnim : public Blitter_32bppOptimized { class Blitter_40bppAnim : public Blitter_32bppOptimized {
public: public:
void SetPixel(void *video, int x, int y, uint8_t colour) override; void SetPixel(void *video, int x, int y, PixelColour colour) override;
void DrawRect(void *video, int width, int height, uint8_t colour) override; void DrawRect(void *video, int width, int height, PixelColour colour) override;
void DrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, uint8_t colour, int width, int dash) override; void DrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, PixelColour colour, int width, int dash) override;
void CopyFromBuffer(void *video, const void *src, int width, int height) override; void CopyFromBuffer(void *video, const void *src, int width, int height) override;
void CopyToBuffer(const void *video, void *dst, int width, int height) override; void CopyToBuffer(const void *video, void *dst, int width, int height) override;
void CopyImageToBuffer(const void *video, void *dst, int width, int height, int dst_pitch) override; void CopyImageToBuffer(const void *video, void *dst, int width, int height, int dst_pitch) override;

View File

@ -29,23 +29,23 @@ void *Blitter_8bppBase::MoveTo(void *video, int x, int y)
return (uint8_t *)video + x + y * _screen.pitch; return (uint8_t *)video + x + y * _screen.pitch;
} }
void Blitter_8bppBase::SetPixel(void *video, int x, int y, uint8_t colour) void Blitter_8bppBase::SetPixel(void *video, int x, int y, PixelColour colour)
{ {
*((uint8_t *)video + x + y * _screen.pitch) = colour; *((uint8_t *)video + x + y * _screen.pitch) = colour.p;
} }
void Blitter_8bppBase::DrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, uint8_t colour, int width, int dash) void Blitter_8bppBase::DrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, PixelColour colour, int width, int dash)
{ {
this->DrawLineGeneric(x, y, x2, y2, screen_width, screen_height, width, dash, [=](int x, int y) { this->DrawLineGeneric(x, y, x2, y2, screen_width, screen_height, width, dash, [=](int x, int y) {
*((uint8_t *)video + x + y * _screen.pitch) = colour; *((uint8_t *)video + x + y * _screen.pitch) = colour.p;
}); });
} }
void Blitter_8bppBase::DrawRect(void *video, int width, int height, uint8_t colour) void Blitter_8bppBase::DrawRect(void *video, int width, int height, PixelColour colour)
{ {
std::byte *p = static_cast<std::byte *>(video); std::byte *p = static_cast<std::byte *>(video);
do { do {
std::fill_n(p, width, static_cast<std::byte>(colour)); std::fill_n(p, width, static_cast<std::byte>(colour.p));
p += _screen.pitch; p += _screen.pitch;
} while (--height); } while (--height);
} }

View File

@ -18,9 +18,9 @@ public:
uint8_t GetScreenDepth() override { return 8; } uint8_t GetScreenDepth() override { return 8; }
void DrawColourMappingRect(void *dst, int width, int height, PaletteID pal) override; void DrawColourMappingRect(void *dst, int width, int height, PaletteID pal) override;
void *MoveTo(void *video, int x, int y) override; void *MoveTo(void *video, int x, int y) override;
void SetPixel(void *video, int x, int y, uint8_t colour) override; void SetPixel(void *video, int x, int y, PixelColour colour) override;
void DrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, uint8_t colour, int width, int dash) override; void DrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, PixelColour colour, int width, int dash) override;
void DrawRect(void *video, int width, int height, uint8_t colour) override; void DrawRect(void *video, int width, int height, PixelColour colour) override;
void CopyFromBuffer(void *video, const void *src, int width, int height) override; void CopyFromBuffer(void *video, const void *src, int width, int height) override;
void CopyToBuffer(const void *video, void *dst, int width, int height) override; void CopyToBuffer(const void *video, void *dst, int width, int height) override;
void CopyImageToBuffer(const void *video, void *dst, int width, int height, int dst_pitch) override; void CopyImageToBuffer(const void *video, void *dst, int width, int height, int dst_pitch) override;

View File

@ -95,18 +95,18 @@ public:
* @param video The destination pointer (video-buffer). * @param video The destination pointer (video-buffer).
* @param x The x position within video-buffer. * @param x The x position within video-buffer.
* @param y The y position within video-buffer. * @param y The y position within video-buffer.
* @param colour A 8bpp mapping colour. * @param colour A pixel colour.
*/ */
virtual void SetPixel(void *video, int x, int y, uint8_t colour) = 0; virtual void SetPixel(void *video, int x, int y, PixelColour colour) = 0;
/** /**
* Make a single horizontal line in a single colour on the video-buffer. * Make a single horizontal line in a single colour on the video-buffer.
* @param video The destination pointer (video-buffer). * @param video The destination pointer (video-buffer).
* @param width The length of the line. * @param width The length of the line.
* @param height The height of the line. * @param height The height of the line.
* @param colour A 8bpp mapping colour. * @param colour A pixel colour.
*/ */
virtual void DrawRect(void *video, int width, int height, uint8_t colour) = 0; virtual void DrawRect(void *video, int width, int height, PixelColour colour) = 0;
/** /**
* Draw a line with a given colour. * Draw a line with a given colour.
@ -117,11 +117,11 @@ public:
* @param y2 The y coordinate to where the lines goes. * @param y2 The y coordinate to where the lines goes.
* @param screen_width The width of the screen you are drawing in (to avoid buffer-overflows). * @param screen_width The width of the screen you are drawing in (to avoid buffer-overflows).
* @param screen_height The height of the screen you are drawing in (to avoid buffer-overflows). * @param screen_height The height of the screen you are drawing in (to avoid buffer-overflows).
* @param colour A 8bpp mapping colour. * @param colour A pixel colour.
* @param width Line width. * @param width Line width.
* @param dash Length of dashes for dashed lines. 0 means solid line. * @param dash Length of dashes for dashed lines. 0 means solid line.
*/ */
virtual void DrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, uint8_t colour, int width, int dash = 0) = 0; virtual void DrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, PixelColour colour, int width, int dash = 0) = 0;
/** /**
* Copy from a buffer to the screen. * Copy from a buffer to the screen.

View File

@ -20,9 +20,9 @@ public:
void DrawColourMappingRect(void *, int, int, PaletteID) override {}; void DrawColourMappingRect(void *, int, int, PaletteID) override {};
Sprite *Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) override; Sprite *Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) override;
void *MoveTo(void *, int, int) override { return nullptr; }; void *MoveTo(void *, int, int) override { return nullptr; };
void SetPixel(void *, int, int, uint8_t) override {}; void SetPixel(void *, int, int, PixelColour) override {};
void DrawRect(void *, int, int, uint8_t) override {}; void DrawRect(void *, int, int, PixelColour) override {};
void DrawLine(void *, int, int, int, int, int, int, uint8_t, int, int) override {}; void DrawLine(void *, int, int, int, int, int, int, PixelColour, int, int) override {};
void CopyFromBuffer(void *, const void *, int, int) override {}; void CopyFromBuffer(void *, const void *, int, int) override {};
void CopyToBuffer(const void *, void *, int, int) override {}; void CopyToBuffer(const void *, void *, int, int) override {};
void CopyImageToBuffer(const void *, void *, int, int, int) override {}; void CopyImageToBuffer(const void *, void *, int, int, int) override {};

View File

@ -60,8 +60,8 @@ public:
void DrawWidget(const Rect &r, WidgetID) const override void DrawWidget(const Rect &r, WidgetID) const override
{ {
GfxFillRect(r.left, r.top, r.right, r.bottom, 4, FILLRECT_OPAQUE); GfxFillRect(r.left, r.top, r.right, r.bottom, PixelColour{4}, FILLRECT_OPAQUE);
GfxFillRect(r.left, r.top, r.right, r.bottom, 0, FILLRECT_CHECKER); GfxFillRect(r.left, r.top, r.right, r.bottom, PixelColour{0}, FILLRECT_CHECKER);
} }
}; };
@ -385,10 +385,10 @@ bool HandleBootstrap()
/* Initialise the palette. The biggest step is 'faking' some recolour sprites. /* Initialise the palette. The biggest step is 'faking' some recolour sprites.
* This way the mauve and gray colours work and we can show the user interface. */ * This way the mauve and gray colours work and we can show the user interface. */
GfxInitPalettes(); GfxInitPalettes();
static const int offsets[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x80, 0, 0, 0, 0x04, 0x08 }; static const uint8_t offsets[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x80, 0, 0, 0, 0x04, 0x08 };
for (Colours i = COLOUR_BEGIN; i != COLOUR_END; i++) { for (Colours i = COLOUR_BEGIN; i != COLOUR_END; i++) {
for (ColourShade j = SHADE_BEGIN; j < SHADE_END; j++) { for (ColourShade j = SHADE_BEGIN; j < SHADE_END; j++) {
SetColourGradient(i, j, offsets[i] + j); SetColourGradient(i, j, PixelColour(offsets[i] + j));
} }
} }

View File

@ -956,7 +956,7 @@ void DrawEngineList(VehicleType type, const Rect &r, const GUIEngineList &eng_li
int sprite_right = GetVehicleImageCellSize(type, EIT_PURCHASE).extend_right; int sprite_right = GetVehicleImageCellSize(type, EIT_PURCHASE).extend_right;
int sprite_width = sprite_left + sprite_right; int sprite_width = sprite_left + sprite_right;
int circle_width = std::max(GetScaledSpriteSize(SPR_CIRCLE_FOLDED).width, GetScaledSpriteSize(SPR_CIRCLE_UNFOLDED).width); int circle_width = std::max(GetScaledSpriteSize(SPR_CIRCLE_FOLDED).width, GetScaledSpriteSize(SPR_CIRCLE_UNFOLDED).width);
int linecolour = GetColourGradient(COLOUR_ORANGE, SHADE_NORMAL); PixelColour linecolour = GetColourGradient(COLOUR_ORANGE, SHADE_NORMAL);
auto badge_column_widths = badge_classes.GetColumnWidths(); auto badge_column_widths = badge_classes.GetColumnWidths();

View File

@ -410,7 +410,7 @@ void VehicleCargoList::AgeCargo()
return (accepted && cp->first_station != current_station) ? MTA_DELIVER : MTA_KEEP; return (accepted && cp->first_station != current_station) ? MTA_DELIVER : MTA_KEEP;
} else if (cargo_next == current_station) { } else if (cargo_next == current_station) {
return MTA_DELIVER; return MTA_DELIVER;
} else if (next_station.Contains(cargo_next.base())) { } else if (next_station.Contains(cargo_next)) {
return MTA_KEEP; return MTA_KEEP;
} else { } else {
return MTA_TRANSFER; return MTA_TRANSFER;
@ -470,7 +470,7 @@ bool VehicleCargoList::Stage(bool accepted, StationID current_station, StationID
new_shares.ChangeShare(current_station, INT_MIN); new_shares.ChangeShare(current_station, INT_MIN);
StationIDStack excluded = next_station; StationIDStack excluded = next_station;
while (!excluded.IsEmpty() && !new_shares.GetShares()->empty()) { while (!excluded.IsEmpty() && !new_shares.GetShares()->empty()) {
new_shares.ChangeShare(StationID{excluded.Pop()}, INT_MIN); new_shares.ChangeShare(excluded.Pop(), INT_MIN);
} }
if (new_shares.GetShares()->empty()) { if (new_shares.GetShares()->empty()) {
cargo_next = StationID::Invalid(); cargo_next = StationID::Invalid();
@ -743,7 +743,7 @@ uint StationCargoList::ShiftCargo(Taction action, StationIDStack next, bool incl
{ {
uint max_move = action.MaxMove(); uint max_move = action.MaxMove();
while (!next.IsEmpty()) { while (!next.IsEmpty()) {
this->ShiftCargo(action, StationID{next.Pop()}); this->ShiftCargo(action, next.Pop());
if (action.MaxMove() == 0) break; if (action.MaxMove() == 0) break;
} }
if (include_invalid && action.MaxMove() > 0) { if (include_invalid && action.MaxMove() > 0) {
@ -853,7 +853,7 @@ uint StationCargoList::Load(uint max_move, VehicleCargoList *dest, StationIDStac
*/ */
uint StationCargoList::Reroute(uint max_move, StationCargoList *dest, StationID avoid, StationID avoid2, const GoodsEntry *ge) uint StationCargoList::Reroute(uint max_move, StationCargoList *dest, StationID avoid, StationID avoid2, const GoodsEntry *ge)
{ {
return this->ShiftCargo(StationCargoReroute(this, dest, max_move, avoid, avoid2, ge), avoid.base(), false); return this->ShiftCargo(StationCargoReroute(this, dest, max_move, avoid, avoid2, ge), avoid, false);
} }
/* /*

View File

@ -555,7 +555,7 @@ public:
inline bool HasCargoFor(StationIDStack next) const inline bool HasCargoFor(StationIDStack next) const
{ {
while (!next.IsEmpty()) { while (!next.IsEmpty()) {
if (this->packets.find(StationID{next.Pop()}) != this->packets.end()) return true; if (this->packets.find(next.Pop()) != this->packets.end()) return true;
} }
/* Packets for StationID::Invalid() can go anywhere. */ /* Packets for StationID::Invalid() can go anywhere. */
return this->packets.find(StationID::Invalid()) != this->packets.end(); return this->packets.find(StationID::Invalid()) != this->packets.end();

View File

@ -74,8 +74,8 @@ static const uint TOWN_PRODUCTION_DIVISOR = 256;
struct CargoSpec { struct CargoSpec {
CargoLabel label; ///< Unique label of the cargo type. CargoLabel label; ///< Unique label of the cargo type.
uint8_t bitnum = INVALID_CARGO_BITNUM; ///< Cargo bit number, is #INVALID_CARGO_BITNUM for a non-used spec. uint8_t bitnum = INVALID_CARGO_BITNUM; ///< Cargo bit number, is #INVALID_CARGO_BITNUM for a non-used spec.
uint8_t legend_colour; PixelColour legend_colour;
uint8_t rating_colour; PixelColour rating_colour;
uint8_t weight; ///< Weight of a single unit of this cargo type in 1/16 ton (62.5 kg). uint8_t weight; ///< Weight of a single unit of this cargo type in 1/16 ton (62.5 kg).
uint16_t multiplier = 0x100; ///< Capacity multiplier for vehicles. (8 fractional bits) uint16_t multiplier = 0x100; ///< Capacity multiplier for vehicles. (8 fractional bits)
CargoClasses classes; ///< Classes of this cargo type. @see CargoClass CargoClasses classes; ///< Classes of this cargo type. @see CargoClass

View File

@ -152,8 +152,8 @@ void SetLocalCompany(CompanyID new_company)
*/ */
TextColour GetDrawStringCompanyColour(CompanyID company) TextColour GetDrawStringCompanyColour(CompanyID company)
{ {
if (!Company::IsValidID(company)) return (TextColour)GetColourGradient(COLOUR_WHITE, SHADE_NORMAL) | TC_IS_PALETTE_COLOUR; if (!Company::IsValidID(company)) return GetColourGradient(COLOUR_WHITE, SHADE_NORMAL).ToTextColour();
return (TextColour)GetColourGradient(_company_colours[company], SHADE_NORMAL) | TC_IS_PALETTE_COLOUR; return GetColourGradient(_company_colours[company], SHADE_NORMAL).ToTextColour();
} }
/** /**

View File

@ -544,7 +544,7 @@ bool IsValidConsoleColour(TextColour c)
* colour gradient, so it must be one of those. */ * colour gradient, so it must be one of those. */
c &= ~TC_IS_PALETTE_COLOUR; c &= ~TC_IS_PALETTE_COLOUR;
for (Colours i = COLOUR_BEGIN; i < COLOUR_END; i++) { for (Colours i = COLOUR_BEGIN; i < COLOUR_END; i++) {
if (GetColourGradient(i, SHADE_NORMAL) == c) return true; if (GetColourGradient(i, SHADE_NORMAL).p == c) return true;
} }
return false; return false;

View File

@ -113,13 +113,14 @@ struct SmallStackItem {
* index types of the same length. * index types of the same length.
* @tparam Titem Value type to be used. * @tparam Titem Value type to be used.
* @tparam Tindex Index type to use for the pool. * @tparam Tindex Index type to use for the pool.
* @tparam Tinvalid Invalid item to keep at the bottom of each stack. * @tparam Tinvalid_value Value to construct invalid item to keep at the bottom of each stack.
* @tparam Tgrowth_step Growth step for pool. * @tparam Tgrowth_step Growth step for pool.
* @tparam Tmax_size Maximum size for pool. * @tparam Tmax_size Maximum size for pool.
*/ */
template <typename Titem, typename Tindex, Titem Tinvalid, Tindex Tgrowth_step, Tindex Tmax_size> template <typename Titem, typename Tindex, auto Tinvalid_value, Tindex Tgrowth_step, Tindex Tmax_size>
class SmallStack : public SmallStackItem<Titem, Tindex> { class SmallStack : public SmallStackItem<Titem, Tindex> {
public: public:
static constexpr Titem Tinvalid{Tinvalid_value};
typedef SmallStackItem<Titem, Tindex> Item; typedef SmallStackItem<Titem, Tindex> Item;

View File

@ -413,7 +413,7 @@ struct DepotWindow : Window {
*/ */
if (this->type == VEH_TRAIN && _consistent_train_width != 0) { if (this->type == VEH_TRAIN && _consistent_train_width != 0) {
int w = ScaleSpriteTrad(2 * _consistent_train_width); int w = ScaleSpriteTrad(2 * _consistent_train_width);
int col = GetColourGradient(wid->colour, SHADE_NORMAL); PixelColour col = GetColourGradient(wid->colour, SHADE_NORMAL);
Rect image = ir.Indent(this->header_width, rtl).Indent(this->count_width, !rtl); Rect image = ir.Indent(this->header_width, rtl).Indent(this->count_width, !rtl);
int first_line = w + (-this->hscroll->GetPosition()) % w; int first_line = w + (-this->hscroll->GetPosition()) % w;
if (rtl) { if (rtl) {

View File

@ -37,8 +37,8 @@ public:
void Draw(const Rect &full, const Rect &, bool, int, Colours bg_colour) const override void Draw(const Rect &full, const Rect &, bool, int, Colours bg_colour) const override
{ {
uint8_t c1 = GetColourGradient(bg_colour, SHADE_DARK); PixelColour c1 = GetColourGradient(bg_colour, SHADE_DARK);
uint8_t c2 = GetColourGradient(bg_colour, SHADE_LIGHTEST); PixelColour c2 = GetColourGradient(bg_colour, SHADE_LIGHTEST);
int mid = CentreBounds(full.top, full.bottom, 0); int mid = CentreBounds(full.top, full.bottom, 0);
GfxFillRect(full.left, mid - WidgetDimensions::scaled.bevel.bottom, full.right, mid - 1, c1); GfxFillRect(full.left, mid - WidgetDimensions::scaled.bevel.bottom, full.right, mid - 1, c1);

View File

@ -70,16 +70,6 @@ public:
*/ */
virtual int GetFontSize() const { return this->height; } virtual int GetFontSize() const { return this->height; }
/**
* Map a SpriteID to the key
* @param key The key to map to.
* @param sprite The sprite that is being mapped.
*/
virtual void SetUnicodeGlyph(char32_t key, SpriteID sprite) = 0;
/** Initialize the glyph map */
virtual void InitializeUnicodeGlyphMap() = 0;
/** Clear the font cache. */ /** Clear the font cache. */
virtual void ClearFontCache() = 0; virtual void ClearFontCache() = 0;
@ -153,20 +143,6 @@ public:
virtual bool IsBuiltInFont() = 0; virtual bool IsBuiltInFont() = 0;
}; };
/** Map a SpriteID to the font size and key */
inline void SetUnicodeGlyph(FontSize size, char32_t key, SpriteID sprite)
{
FontCache::Get(size)->SetUnicodeGlyph(key, sprite);
}
/** Initialize the glyph map */
inline void InitializeUnicodeGlyphMap()
{
for (FontSize fs = FS_BEGIN; fs < FS_END; fs++) {
FontCache::Get(fs)->InitializeUnicodeGlyphMap();
}
}
inline void ClearFontCache(FontSizes fontsizes) inline void ClearFontCache(FontSizes fontsizes)
{ {
for (FontSize fs : fontsizes) { for (FontSize fs : fontsizes) {
@ -237,4 +213,8 @@ void UninitFontCache();
bool GetFontAAState(); bool GetFontAAState();
void SetFont(FontSize fontsize, const std::string &font, uint size); void SetFont(FontSize fontsize, const std::string &font, uint size);
/* Implemented in spritefontcache.cpp */
void InitializeUnicodeGlyphMap();
void SetUnicodeGlyph(FontSize size, char32_t key, SpriteID sprite);
#endif /* FONTCACHE_H */ #endif /* FONTCACHE_H */

View File

@ -10,6 +10,7 @@
#include "../stdafx.h" #include "../stdafx.h"
#include "../fontcache.h" #include "../fontcache.h"
#include "../gfx_layout.h" #include "../gfx_layout.h"
#include "../string_func.h"
#include "../zoom_func.h" #include "../zoom_func.h"
#include "spritefontcache.h" #include "spritefontcache.h"
@ -31,41 +32,43 @@ static int ScaleFontTrad(int value)
return UnScaleByZoom(value * ZOOM_BASE, _font_zoom); return UnScaleByZoom(value * ZOOM_BASE, _font_zoom);
} }
/** static std::array<std::unordered_map<char32_t, SpriteID>, FS_END> _char_maps{}; ///< Glyph map for each font size.
* Create a new sprite font cache.
* @param fs The font size to create the cache for.
*/
SpriteFontCache::SpriteFontCache(FontSize fs) : FontCache(fs)
{
this->InitializeUnicodeGlyphMap();
this->height = ScaleGUITrad(FontCache::GetDefaultFontHeight(this->fs));
this->ascender = (this->height - ScaleFontTrad(FontCache::GetDefaultFontHeight(this->fs))) / 2;
}
/** /**
* Get SpriteID associated with a character. * Get SpriteID associated with a character.
* @param key Character to find. * @param key Character to find.
* @return SpriteID for character, or 0 if not present. * @return SpriteID for character, or 0 if not present.
*/ */
SpriteID SpriteFontCache::GetUnicodeGlyph(char32_t key) static SpriteID GetUnicodeGlyph(FontSize fs, char32_t key)
{ {
const auto found = this->char_map.find(key); auto found = _char_maps[fs].find(key);
if (found == std::end(this->char_map)) return 0; if (found != std::end(_char_maps[fs])) return found->second;
return found->second; return 0;
} }
void SpriteFontCache::SetUnicodeGlyph(char32_t key, SpriteID sprite) /**
* Set the SpriteID for a unicode character.
* @param fs Font size to set.
* @param key Unicode character to set.
* @param sprite SpriteID of character.
*/
void SetUnicodeGlyph(FontSize fs, char32_t key, SpriteID sprite)
{ {
this->char_map[key] = sprite; _char_maps[fs][key] = sprite;
} }
void SpriteFontCache::InitializeUnicodeGlyphMap() /**
* Initialize the glyph map for a font size.
* This populates the glyph map with the baseset font sprites.
* @param fs Font size to initialize.
*/
void InitializeUnicodeGlyphMap(FontSize fs)
{ {
/* Clear out existing glyph map if it exists */ /* Clear out existing glyph map if it exists */
this->char_map.clear(); _char_maps[fs].clear();
SpriteID base; SpriteID base;
switch (this->fs) { switch (fs) {
default: NOT_REACHED(); default: NOT_REACHED();
case FS_MONO: // Use normal as default for mono spaced font case FS_MONO: // Use normal as default for mono spaced font
case FS_NORMAL: base = SPR_ASCII_SPACE; break; case FS_NORMAL: base = SPR_ASCII_SPACE; break;
@ -76,24 +79,45 @@ void SpriteFontCache::InitializeUnicodeGlyphMap()
for (uint i = ASCII_LETTERSTART; i < 256; i++) { for (uint i = ASCII_LETTERSTART; i < 256; i++) {
SpriteID sprite = base + i - ASCII_LETTERSTART; SpriteID sprite = base + i - ASCII_LETTERSTART;
if (!SpriteExists(sprite)) continue; if (!SpriteExists(sprite)) continue;
this->SetUnicodeGlyph(i, sprite); SetUnicodeGlyph(fs, i, sprite);
this->SetUnicodeGlyph(i + SCC_SPRITE_START, sprite); SetUnicodeGlyph(fs, i + SCC_SPRITE_START, sprite);
} }
/* Modify map to move non-standard glyphs to a better unicode codepoint. */
for (const auto &unicode_map : _default_unicode_map) { for (const auto &unicode_map : _default_unicode_map) {
uint8_t key = unicode_map.key; uint8_t key = unicode_map.key;
if (key == CLRA) { if (key == CLRA) {
/* Clear the glyph. This happens if the glyph at this code point /* Clear the glyph. This happens if the glyph at this code point
* is non-standard and should be accessed by an SCC_xxx enum * is non-standard and should be accessed by an SCC_xxx enum
* entry only. */ * entry only. */
this->SetUnicodeGlyph(unicode_map.code, 0); SetUnicodeGlyph(fs, unicode_map.code, 0);
} else { } else {
SpriteID sprite = base + key - ASCII_LETTERSTART; SpriteID sprite = base + key - ASCII_LETTERSTART;
this->SetUnicodeGlyph(unicode_map.code, sprite); SetUnicodeGlyph(fs, unicode_map.code, sprite);
} }
} }
} }
/**
* Initialize the glyph map.
*/
void InitializeUnicodeGlyphMap()
{
for (FontSize fs = FS_BEGIN; fs < FS_END; fs++) {
InitializeUnicodeGlyphMap(fs);
}
}
/**
* Create a new sprite font cache.
* @param fs The font size to create the cache for.
*/
SpriteFontCache::SpriteFontCache(FontSize fs) : FontCache(fs)
{
this->height = ScaleGUITrad(FontCache::GetDefaultFontHeight(this->fs));
this->ascender = (this->height - ScaleFontTrad(FontCache::GetDefaultFontHeight(this->fs))) / 2;
}
void SpriteFontCache::ClearFontCache() void SpriteFontCache::ClearFontCache()
{ {
Layouter::ResetFontCache(this->fs); Layouter::ResetFontCache(this->fs);
@ -104,21 +128,21 @@ void SpriteFontCache::ClearFontCache()
const Sprite *SpriteFontCache::GetGlyph(GlyphID key) const Sprite *SpriteFontCache::GetGlyph(GlyphID key)
{ {
SpriteID sprite = static_cast<SpriteID>(key & ~SPRITE_GLYPH); SpriteID sprite = static_cast<SpriteID>(key & ~SPRITE_GLYPH);
if (sprite == 0) sprite = this->GetUnicodeGlyph('?'); if (sprite == 0) sprite = GetUnicodeGlyph(this->fs, '?');
return GetSprite(sprite, SpriteType::Font); return GetSprite(sprite, SpriteType::Font);
} }
uint SpriteFontCache::GetGlyphWidth(GlyphID key) uint SpriteFontCache::GetGlyphWidth(GlyphID key)
{ {
SpriteID sprite = static_cast<SpriteID>(key & ~SPRITE_GLYPH); SpriteID sprite = static_cast<SpriteID>(key & ~SPRITE_GLYPH);
if (sprite == 0) sprite = this->GetUnicodeGlyph('?'); if (sprite == 0) sprite = GetUnicodeGlyph(this->fs, '?');
return SpriteExists(sprite) ? GetSprite(sprite, SpriteType::Font)->width + ScaleFontTrad(this->fs != FS_NORMAL ? 1 : 0) : 0; return SpriteExists(sprite) ? GetSprite(sprite, SpriteType::Font)->width + ScaleFontTrad(this->fs != FS_NORMAL ? 1 : 0) : 0;
} }
GlyphID SpriteFontCache::MapCharToGlyph(char32_t key, [[maybe_unused]] bool allow_fallback) GlyphID SpriteFontCache::MapCharToGlyph(char32_t key, [[maybe_unused]] bool allow_fallback)
{ {
assert(IsPrintable(key)); assert(IsPrintable(key));
SpriteID sprite = this->GetUnicodeGlyph(key); SpriteID sprite = GetUnicodeGlyph(this->fs, key);
if (sprite == 0) return 0; if (sprite == 0) return 0;
return SPRITE_GLYPH | sprite; return SPRITE_GLYPH | sprite;
} }

View File

@ -10,15 +10,12 @@
#ifndef SPRITEFONTCACHE_H #ifndef SPRITEFONTCACHE_H
#define SPRITEFONTCACHE_H #define SPRITEFONTCACHE_H
#include "../string_func.h"
#include "../fontcache.h" #include "../fontcache.h"
/** Font cache for fonts that are based on a freetype font. */ /** Font cache for fonts that are based on a freetype font. */
class SpriteFontCache : public FontCache { class SpriteFontCache : public FontCache {
public: public:
SpriteFontCache(FontSize fs); SpriteFontCache(FontSize fs);
void SetUnicodeGlyph(char32_t key, SpriteID sprite) override;
void InitializeUnicodeGlyphMap() override;
void ClearFontCache() override; void ClearFontCache() override;
const Sprite *GetGlyph(GlyphID key) override; const Sprite *GetGlyph(GlyphID key) override;
uint GetGlyphWidth(GlyphID key) override; uint GetGlyphWidth(GlyphID key) override;
@ -26,10 +23,6 @@ public:
GlyphID MapCharToGlyph(char32_t key, bool allow_fallback = true) override; GlyphID MapCharToGlyph(char32_t key, bool allow_fallback = true) override;
std::string GetFontName() override { return "sprite"; } std::string GetFontName() override { return "sprite"; }
bool IsBuiltInFont() override { return true; } bool IsBuiltInFont() override { return true; }
private:
std::unordered_map<char32_t, SpriteID> char_map{}; ///< Mapping of characters to sprite IDs.
SpriteID GetUnicodeGlyph(char32_t key);
}; };
#endif /* SPRITEFONTCACHE_H */ #endif /* SPRITEFONTCACHE_H */

View File

@ -46,8 +46,6 @@ public:
TrueTypeFontCache(FontSize fs, int pixels); TrueTypeFontCache(FontSize fs, int pixels);
virtual ~TrueTypeFontCache(); virtual ~TrueTypeFontCache();
int GetFontSize() const override { return this->used_size; } int GetFontSize() const override { return this->used_size; }
void SetUnicodeGlyph(char32_t key, SpriteID sprite) override { this->parent->SetUnicodeGlyph(key, sprite); }
void InitializeUnicodeGlyphMap() override { this->parent->InitializeUnicodeGlyphMap(); }
const Sprite *GetGlyph(GlyphID key) override; const Sprite *GetGlyph(GlyphID key) override;
void ClearFontCache() override; void ClearFontCache() override;
uint GetGlyphWidth(GlyphID key) override; uint GetGlyphWidth(GlyphID key) override;

View File

@ -867,9 +867,9 @@ struct FrametimeGraphWindow : Window {
const int x_max = r.right; const int x_max = r.right;
const int y_zero = r.top + (int)this->graph_size.height; const int y_zero = r.top + (int)this->graph_size.height;
const int y_max = r.top; const int y_max = r.top;
const int c_grid = PC_DARK_GREY; const PixelColour c_grid = PC_DARK_GREY;
const int c_lines = PC_BLACK; const PixelColour c_lines = PC_BLACK;
const int c_peak = PC_DARK_RED; const PixelColour c_peak = PC_DARK_RED;
const TimingMeasurement draw_horz_scale = (TimingMeasurement)this->horizontal_scale * TIMESTAMP_PRECISION / 2; const TimingMeasurement draw_horz_scale = (TimingMeasurement)this->horizontal_scale * TIMESTAMP_PRECISION / 2;
const TimingMeasurement draw_vert_scale = (TimingMeasurement)this->vertical_scale; const TimingMeasurement draw_vert_scale = (TimingMeasurement)this->vertical_scale;
@ -959,7 +959,7 @@ struct FrametimeGraphWindow : Window {
/* If the peak value is significantly larger than the average, mark and label it */ /* If the peak value is significantly larger than the average, mark and label it */
if (points_drawn > 0 && peak_value > TIMESTAMP_PRECISION / 100 && 2 * peak_value > 3 * value_sum / points_drawn) { if (points_drawn > 0 && peak_value > TIMESTAMP_PRECISION / 100 && 2 * peak_value > 3 * value_sum / points_drawn) {
TextColour tc_peak = (TextColour)(TC_IS_PALETTE_COLOUR | c_peak); TextColour tc_peak = c_peak.ToTextColour();
GfxFillRect(peak_point.x - 1, peak_point.y - 1, peak_point.x + 1, peak_point.y + 1, c_peak); GfxFillRect(peak_point.x - 1, peak_point.y - 1, peak_point.x + 1, peak_point.y + 1, c_peak);
uint64_t value = peak_value * 1000 / TIMESTAMP_PRECISION; uint64_t value = peak_value * 1000 / TIMESTAMP_PRECISION;
int label_y = std::max(y_max, peak_point.y - GetCharacterHeight(FS_SMALL)); int label_y = std::max(y_max, peak_point.y - GetCharacterHeight(FS_SMALL));

View File

@ -113,7 +113,7 @@ void GfxScroll(int left, int top, int width, int height, int xo, int yo)
* FILLRECT_CHECKER: Like FILLRECT_OPAQUE, but only draw every second pixel (used to grey out things) * FILLRECT_CHECKER: Like FILLRECT_OPAQUE, but only draw every second pixel (used to grey out things)
* FILLRECT_RECOLOUR: Apply a recolour sprite to every pixel in the rectangle currently on screen * FILLRECT_RECOLOUR: Apply a recolour sprite to every pixel in the rectangle currently on screen
*/ */
void GfxFillRect(int left, int top, int right, int bottom, int colour, FillRectMode mode) void GfxFillRect(int left, int top, int right, int bottom, const std::variant<PixelColour, PaletteID> &colour, FillRectMode mode)
{ {
Blitter *blitter = BlitterFactory::GetCurrentBlitter(); Blitter *blitter = BlitterFactory::GetCurrentBlitter();
const DrawPixelInfo *dpi = _cur_dpi; const DrawPixelInfo *dpi = _cur_dpi;
@ -142,17 +142,18 @@ void GfxFillRect(int left, int top, int right, int bottom, int colour, FillRectM
switch (mode) { switch (mode) {
default: // FILLRECT_OPAQUE default: // FILLRECT_OPAQUE
blitter->DrawRect(dst, right, bottom, (uint8_t)colour); blitter->DrawRect(dst, right, bottom, std::get<PixelColour>(colour));
break; break;
case FILLRECT_RECOLOUR: case FILLRECT_RECOLOUR:
blitter->DrawColourMappingRect(dst, right, bottom, GB(colour, 0, PALETTE_WIDTH)); blitter->DrawColourMappingRect(dst, right, bottom, GB(std::get<PaletteID>(colour), 0, PALETTE_WIDTH));
break; break;
case FILLRECT_CHECKER: { case FILLRECT_CHECKER: {
uint8_t bo = (oleft - left + dpi->left + otop - top + dpi->top) & 1; uint8_t bo = (oleft - left + dpi->left + otop - top + dpi->top) & 1;
PixelColour pc = std::get<PixelColour>(colour);
do { do {
for (int i = (bo ^= 1); i < right; i += 2) blitter->SetPixel(dst, i, 0, (uint8_t)colour); for (int i = (bo ^= 1); i < right; i += 2) blitter->SetPixel(dst, i, 0, pc);
dst = blitter->MoveTo(dst, 0, 1); dst = blitter->MoveTo(dst, 0, 1);
} while (--bottom > 0); } while (--bottom > 0);
break; break;
@ -209,7 +210,7 @@ static std::vector<LineSegment> MakePolygonSegments(std::span<const Point> shape
* FILLRECT_CHECKER: Fill every other pixel with the specified colour, in a checkerboard pattern. * FILLRECT_CHECKER: Fill every other pixel with the specified colour, in a checkerboard pattern.
* FILLRECT_RECOLOUR: Apply a recolour sprite to every pixel in the polygon. * FILLRECT_RECOLOUR: Apply a recolour sprite to every pixel in the polygon.
*/ */
void GfxFillPolygon(std::span<const Point> shape, int colour, FillRectMode mode) void GfxFillPolygon(std::span<const Point> shape, const std::variant<PixelColour, PaletteID> &colour, FillRectMode mode)
{ {
Blitter *blitter = BlitterFactory::GetCurrentBlitter(); Blitter *blitter = BlitterFactory::GetCurrentBlitter();
const DrawPixelInfo *dpi = _cur_dpi; const DrawPixelInfo *dpi = _cur_dpi;
@ -278,18 +279,20 @@ void GfxFillPolygon(std::span<const Point> shape, int colour, FillRectMode mode)
void *dst = blitter->MoveTo(dpi->dst_ptr, x1, y); void *dst = blitter->MoveTo(dpi->dst_ptr, x1, y);
switch (mode) { switch (mode) {
default: // FILLRECT_OPAQUE default: // FILLRECT_OPAQUE
blitter->DrawRect(dst, x2 - x1, 1, (uint8_t)colour); blitter->DrawRect(dst, x2 - x1, 1, std::get<PixelColour>(colour));
break; break;
case FILLRECT_RECOLOUR: case FILLRECT_RECOLOUR:
blitter->DrawColourMappingRect(dst, x2 - x1, 1, GB(colour, 0, PALETTE_WIDTH)); blitter->DrawColourMappingRect(dst, x2 - x1, 1, GB(std::get<PaletteID>(colour), 0, PALETTE_WIDTH));
break; break;
case FILLRECT_CHECKER: case FILLRECT_CHECKER: {
/* Fill every other pixel, offset such that the sum of filled pixels' X and Y coordinates is odd. /* Fill every other pixel, offset such that the sum of filled pixels' X and Y coordinates is odd.
* This creates a checkerboard effect. */ * This creates a checkerboard effect. */
PixelColour pc = std::get<PixelColour>(colour);
for (int x = (x1 + y) & 1; x < x2 - x1; x += 2) { for (int x = (x1 + y) & 1; x < x2 - x1; x += 2) {
blitter->SetPixel(dst, x, 0, (uint8_t)colour); blitter->SetPixel(dst, x, 0, pc);
} }
break; break;
}
} }
} }
@ -312,7 +315,7 @@ void GfxFillPolygon(std::span<const Point> shape, int colour, FillRectMode mode)
* @param width Width of the line. * @param width Width of the line.
* @param dash Length of dashes for dashed lines. 0 means solid line. * @param dash Length of dashes for dashed lines. 0 means solid line.
*/ */
static inline void GfxDoDrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, uint8_t colour, int width, int dash = 0) static inline void GfxDoDrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, PixelColour colour, int width, int dash = 0)
{ {
Blitter *blitter = BlitterFactory::GetCurrentBlitter(); Blitter *blitter = BlitterFactory::GetCurrentBlitter();
@ -385,7 +388,7 @@ static inline bool GfxPreprocessLine(DrawPixelInfo *dpi, int &x, int &y, int &x2
return true; return true;
} }
void GfxDrawLine(int x, int y, int x2, int y2, int colour, int width, int dash) void GfxDrawLine(int x, int y, int x2, int y2, PixelColour colour, int width, int dash)
{ {
DrawPixelInfo *dpi = _cur_dpi; DrawPixelInfo *dpi = _cur_dpi;
if (GfxPreprocessLine(dpi, x, y, x2, y2, width)) { if (GfxPreprocessLine(dpi, x, y, x2, y2, width)) {
@ -393,7 +396,7 @@ void GfxDrawLine(int x, int y, int x2, int y2, int colour, int width, int dash)
} }
} }
void GfxDrawLineUnscaled(int x, int y, int x2, int y2, int colour) void GfxDrawLineUnscaled(int x, int y, int x2, int y2, PixelColour colour)
{ {
DrawPixelInfo *dpi = _cur_dpi; DrawPixelInfo *dpi = _cur_dpi;
if (GfxPreprocessLine(dpi, x, y, x2, y2, 1)) { if (GfxPreprocessLine(dpi, x, y, x2, y2, 1)) {
@ -434,7 +437,7 @@ void DrawBox(int x, int y, int dx1, int dy1, int dx2, int dy2, int dx3, int dy3)
* ....V. * ....V.
*/ */
static const uint8_t colour = PC_WHITE; static constexpr PixelColour colour = PC_WHITE;
GfxDrawLineUnscaled(x, y, x + dx1, y + dy1, colour); GfxDrawLineUnscaled(x, y, x + dx1, y + dy1, colour);
GfxDrawLineUnscaled(x, y, x + dx2, y + dy2, colour); GfxDrawLineUnscaled(x, y, x + dx2, y + dy2, colour);
@ -455,7 +458,7 @@ void DrawBox(int x, int y, int dx1, int dy1, int dx2, int dy2, int dx3, int dy3)
* @param width Width of the outline. * @param width Width of the outline.
* @param dash Length of dashes for dashed lines. 0 means solid lines. * @param dash Length of dashes for dashed lines. 0 means solid lines.
*/ */
void DrawRectOutline(const Rect &r, int colour, int width, int dash) void DrawRectOutline(const Rect &r, PixelColour colour, int width, int dash)
{ {
GfxDrawLine(r.left, r.top, r.right, r.top, colour, width, dash); GfxDrawLine(r.left, r.top, r.right, r.top, colour, width, dash);
GfxDrawLine(r.left, r.top, r.left, r.bottom, colour, width, dash); GfxDrawLine(r.left, r.top, r.left, r.bottom, colour, width, dash);
@ -477,7 +480,7 @@ static void SetColourRemap(TextColour colour)
bool raw_colour = (colour & TC_IS_PALETTE_COLOUR) != 0; bool raw_colour = (colour & TC_IS_PALETTE_COLOUR) != 0;
colour &= ~(TC_NO_SHADE | TC_IS_PALETTE_COLOUR | TC_FORCED); colour &= ~(TC_NO_SHADE | TC_IS_PALETTE_COLOUR | TC_FORCED);
_string_colourremap[1] = raw_colour ? (uint8_t)colour : _string_colourmap[colour]; _string_colourremap[1] = raw_colour ? (uint8_t)colour : _string_colourmap[colour].p;
_string_colourremap[2] = no_shade ? 0 : 1; _string_colourremap[2] = no_shade ? 0 : 1;
_colour_remap_ptr = _string_colourremap; _colour_remap_ptr = _string_colourremap;
} }
@ -637,7 +640,7 @@ static int DrawLayoutLine(const ParagraphLayouter::Line &line, int y, int left,
} }
if (underline) { if (underline) {
GfxFillRect(left, y + h, right, y + h + WidgetDimensions::scaled.bevel.top - 1, _string_colourremap[1]); GfxFillRect(left, y + h, right, y + h + WidgetDimensions::scaled.bevel.top - 1, PixelColour{_string_colourremap[1]});
} }
return (align & SA_HOR_MASK) == SA_RIGHT ? left : right; return (align & SA_HOR_MASK) == SA_RIGHT ? left : right;

View File

@ -103,11 +103,11 @@ bool DrawStringMultiLineWithClipping(int left, int right, int top, int bottom, s
void DrawCharCentered(char32_t c, const Rect &r, TextColour colour); void DrawCharCentered(char32_t c, const Rect &r, TextColour colour);
void GfxFillRect(int left, int top, int right, int bottom, int colour, FillRectMode mode = FILLRECT_OPAQUE); void GfxFillRect(int left, int top, int right, int bottom, const std::variant<PixelColour, PaletteID> &colour, FillRectMode mode = FILLRECT_OPAQUE);
void GfxFillPolygon(std::span<const Point> shape, int colour, FillRectMode mode = FILLRECT_OPAQUE); void GfxFillPolygon(std::span<const Point> shape, const std::variant<PixelColour, PaletteID> &colour, FillRectMode mode = FILLRECT_OPAQUE);
void GfxDrawLine(int left, int top, int right, int bottom, int colour, int width = 1, int dash = 0); void GfxDrawLine(int left, int top, int right, int bottom, PixelColour colour, int width = 1, int dash = 0);
void DrawBox(int x, int y, int dx1, int dy1, int dx2, int dy2, int dx3, int dy3); void DrawBox(int x, int y, int dx1, int dy1, int dx2, int dy2, int dx3, int dy3);
void DrawRectOutline(const Rect &r, int colour, int width = 1, int dash = 0); void DrawRectOutline(const Rect &r, PixelColour colour, int width = 1, int dash = 0);
/* Versions of DrawString/DrawStringMultiLine that accept a Rect instead of separate left, right, top and bottom parameters. */ /* Versions of DrawString/DrawStringMultiLine that accept a Rect instead of separate left, right, top and bottom parameters. */
inline int DrawString(const Rect &r, std::string_view str, TextColour colour = TC_FROMSTRING, StringAlignment align = SA_LEFT, bool underline = false, FontSize fontsize = FS_NORMAL) inline int DrawString(const Rect &r, std::string_view str, TextColour colour = TC_FROMSTRING, StringAlignment align = SA_LEFT, bool underline = false, FontSize fontsize = FS_NORMAL)
@ -135,7 +135,7 @@ inline bool DrawStringMultiLineWithClipping(const Rect &r, std::string_view str,
return DrawStringMultiLineWithClipping(r.left, r.right, r.top, r.bottom, str, colour, align, underline, fontsize); return DrawStringMultiLineWithClipping(r.left, r.right, r.top, r.bottom, str, colour, align, underline, fontsize);
} }
inline void GfxFillRect(const Rect &r, int colour, FillRectMode mode = FILLRECT_OPAQUE) inline void GfxFillRect(const Rect &r, const std::variant<PixelColour, PaletteID> &colour, FillRectMode mode = FILLRECT_OPAQUE)
{ {
GfxFillRect(r.left, r.top, r.right, r.bottom, colour, mode); GfxFillRect(r.left, r.top, r.right, r.bottom, colour, mode);
} }

View File

@ -245,7 +245,6 @@ using Colour = std::conditional_t<std::endian::native == std::endian::little, Co
static_assert(sizeof(Colour) == sizeof(uint32_t)); static_assert(sizeof(Colour) == sizeof(uint32_t));
/** Available font sizes */ /** Available font sizes */
enum FontSize : uint8_t { enum FontSize : uint8_t {
FS_NORMAL, ///< Index of the normal font in the font tables. FS_NORMAL, ///< Index of the normal font in the font tables.
@ -403,4 +402,14 @@ enum StringAlignment : uint8_t {
}; };
DECLARE_ENUM_AS_BIT_SET(StringAlignment) DECLARE_ENUM_AS_BIT_SET(StringAlignment)
/** Colour for pixel/line drawing. */
struct PixelColour {
uint8_t p; ///< Palette index.
constexpr PixelColour() : p(0) {}
explicit constexpr PixelColour(uint8_t p) : p(p) {}
constexpr inline TextColour ToTextColour() const { return static_cast<TextColour>(this->p) | TC_IS_PALETTE_COLOUR; }
};
#endif /* GFX_TYPE_H */ #endif /* GFX_TYPE_H */

View File

@ -165,11 +165,11 @@ struct ValuesInterval {
struct BaseGraphWindow : Window { struct BaseGraphWindow : Window {
protected: protected:
static const int GRAPH_MAX_DATASETS = 64; static const int GRAPH_MAX_DATASETS = 64;
static const int GRAPH_BASE_COLOUR = GREY_SCALE(2); static constexpr PixelColour GRAPH_BASE_COLOUR = GREY_SCALE(2);
static const int GRAPH_GRID_COLOUR = GREY_SCALE(3); static constexpr PixelColour GRAPH_GRID_COLOUR = GREY_SCALE(3);
static const int GRAPH_AXIS_LINE_COLOUR = GREY_SCALE(1); static constexpr PixelColour GRAPH_AXIS_LINE_COLOUR = GREY_SCALE(1);
static const int GRAPH_ZERO_LINE_COLOUR = GREY_SCALE(8); static constexpr PixelColour GRAPH_ZERO_LINE_COLOUR = GREY_SCALE(8);
static const int GRAPH_YEAR_LINE_COLOUR = GREY_SCALE(5); static constexpr PixelColour GRAPH_YEAR_LINE_COLOUR = GREY_SCALE(5);
static const int GRAPH_NUM_MONTHS = 24; ///< Number of months displayed in the graph. static const int GRAPH_NUM_MONTHS = 24; ///< Number of months displayed in the graph.
static const int GRAPH_PAYMENT_RATE_STEPS = 20; ///< Number of steps on Payment rate graph. static const int GRAPH_PAYMENT_RATE_STEPS = 20; ///< Number of steps on Payment rate graph.
static const int PAYMENT_GRAPH_X_STEP_DAYS = 10; ///< X-axis step label for cargo payment rates "Days in transit". static const int PAYMENT_GRAPH_X_STEP_DAYS = 10; ///< X-axis step label for cargo payment rates "Days in transit".
@ -204,7 +204,7 @@ protected:
struct DataSet { struct DataSet {
std::array<OverflowSafeInt64, GRAPH_NUM_MONTHS> values; std::array<OverflowSafeInt64, GRAPH_NUM_MONTHS> values;
uint8_t colour; PixelColour colour;
uint8_t exclude_bit; uint8_t exclude_bit;
uint8_t range_bit; uint8_t range_bit;
uint8_t dash; uint8_t dash;
@ -398,7 +398,7 @@ protected:
/* Draw the grid lines. */ /* Draw the grid lines. */
int gridline_width = WidgetDimensions::scaled.bevel.top; int gridline_width = WidgetDimensions::scaled.bevel.top;
int grid_colour = GRAPH_GRID_COLOUR; PixelColour grid_colour = GRAPH_GRID_COLOUR;
/* Don't draw the first line, as that's where the axis will be. */ /* Don't draw the first line, as that's where the axis will be. */
if (rtl) { if (rtl) {
@ -524,7 +524,7 @@ protected:
uint pointoffs1 = pointwidth / 2; uint pointoffs1 = pointwidth / 2;
uint pointoffs2 = pointwidth - pointoffs1; uint pointoffs2 = pointwidth - pointoffs1;
auto draw_dataset = [&](const DataSet &dataset, uint8_t colour) { auto draw_dataset = [&](const DataSet &dataset, PixelColour colour) {
if (HasBit(this->excluded_data, dataset.exclude_bit)) return; if (HasBit(this->excluded_data, dataset.exclude_bit)) return;
if (HasBit(this->excluded_range, dataset.range_bit)) return; if (HasBit(this->excluded_range, dataset.range_bit)) return;
@ -1222,7 +1222,7 @@ struct BaseCargoGraphWindow : BaseGraphWindow {
/* Cargo-colour box with outline */ /* Cargo-colour box with outline */
const Rect cargo = text.WithWidth(this->legend_width, rtl); const Rect cargo = text.WithWidth(this->legend_width, rtl);
GfxFillRect(cargo, PC_BLACK); GfxFillRect(cargo, PC_BLACK);
uint8_t pc = cs->legend_colour; PixelColour pc = cs->legend_colour;
if (this->highlight_data == cs->Index()) pc = this->highlight_state ? PC_WHITE : PC_BLACK; if (this->highlight_data == cs->Index()) pc = this->highlight_state ? PC_WHITE : PC_BLACK;
GfxFillRect(cargo.Shrink(WidgetDimensions::scaled.bevel), pc); GfxFillRect(cargo.Shrink(WidgetDimensions::scaled.bevel), pc);
@ -1499,8 +1499,8 @@ struct PerformanceRatingDetailWindow : Window {
ScoreID score_type = (ScoreID)(widget - WID_PRD_SCORE_FIRST); ScoreID score_type = (ScoreID)(widget - WID_PRD_SCORE_FIRST);
/* The colours used to show how the progress is going */ /* The colours used to show how the progress is going */
int colour_done = GetColourGradient(COLOUR_GREEN, SHADE_NORMAL); PixelColour colour_done = GetColourGradient(COLOUR_GREEN, SHADE_NORMAL);
int colour_notdone = GetColourGradient(COLOUR_RED, SHADE_NORMAL); PixelColour colour_notdone = GetColourGradient(COLOUR_RED, SHADE_NORMAL);
/* Draw all the score parts */ /* Draw all the score parts */
int64_t val = _score_part[company][score_type]; int64_t val = _score_part[company][score_type];

View File

@ -305,7 +305,7 @@ private:
const int offset = (rtl ? -(int)this->column_size[VGC_FOLD].width : (int)this->column_size[VGC_FOLD].width) / 2; const int offset = (rtl ? -(int)this->column_size[VGC_FOLD].width : (int)this->column_size[VGC_FOLD].width) / 2;
const int level_width = rtl ? -WidgetDimensions::scaled.hsep_indent : WidgetDimensions::scaled.hsep_indent; const int level_width = rtl ? -WidgetDimensions::scaled.hsep_indent : WidgetDimensions::scaled.hsep_indent;
const int linecolour = GetColourGradient(COLOUR_ORANGE, SHADE_NORMAL); const PixelColour linecolour = GetColourGradient(COLOUR_ORANGE, SHADE_NORMAL);
if (indent > 0) { if (indent > 0) {
/* Draw tree continuation lines. */ /* Draw tree continuation lines. */

View File

@ -1986,8 +1986,8 @@ struct CargoesField {
static Dimension cargo_space; static Dimension cargo_space;
static Dimension cargo_stub; static Dimension cargo_stub;
static const int INDUSTRY_LINE_COLOUR; static const PixelColour INDUSTRY_LINE_COLOUR;
static const int CARGO_LINE_COLOUR; static const PixelColour CARGO_LINE_COLOUR;
static int small_height, normal_height; static int small_height, normal_height;
static int cargo_field_width; static int cargo_field_width;
@ -2395,8 +2395,8 @@ int CargoesField::vert_inter_industry_space; ///< Amount of space between two in
int CargoesField::blob_distance; ///< Distance of the industry legend colour from the edge of the industry box. int CargoesField::blob_distance; ///< Distance of the industry legend colour from the edge of the industry box.
const int CargoesField::INDUSTRY_LINE_COLOUR = PC_YELLOW; ///< Line colour of the industry type box. const PixelColour CargoesField::INDUSTRY_LINE_COLOUR = PC_YELLOW; ///< Line colour of the industry type box.
const int CargoesField::CARGO_LINE_COLOUR = PC_YELLOW; ///< Line colour around the cargo. const PixelColour CargoesField::CARGO_LINE_COLOUR = PC_YELLOW; ///< Line colour around the cargo.
/** A single row of #CargoesField. */ /** A single row of #CargoesField. */
struct CargoesRow { struct CargoesRow {

View File

@ -119,7 +119,7 @@ struct IndustrySpec {
IndustryLifeTypes life_type; ///< This is also known as Industry production flag, in newgrf specs IndustryLifeTypes life_type; ///< This is also known as Industry production flag, in newgrf specs
LandscapeTypes climate_availability; ///< Bitmask, giving landscape enums as bit position LandscapeTypes climate_availability; ///< Bitmask, giving landscape enums as bit position
IndustryBehaviours behaviour; ///< How this industry will behave, and how others entities can use it IndustryBehaviours behaviour; ///< How this industry will behave, and how others entities can use it
uint8_t map_colour; ///< colour used for the small map PixelColour map_colour; ///< colour used for the small map
StringID name; ///< Displayed name of the industry StringID name; ///< Displayed name of the industry
StringID new_industry_text; ///< Message appearing when the industry is built StringID new_industry_text; ///< Message appearing when the industry is built
StringID closure_text; ///< Message appearing when the industry closes StringID closure_text; ///< Message appearing when the industry closes

View File

@ -30,26 +30,26 @@
* Colours for the various "load" states of links. Ordered from "unused" to * Colours for the various "load" states of links. Ordered from "unused" to
* "overloaded". * "overloaded".
*/ */
const uint8_t LinkGraphOverlay::LINK_COLOURS[][12] = { const PixelColour LinkGraphOverlay::LINK_COLOURS[][12] = {
{ {
0x0f, 0xd1, 0xd0, 0x57, PixelColour{0x0f}, PixelColour{0xd1}, PixelColour{0xd0}, PixelColour{0x57},
0x55, 0x53, 0xbf, 0xbd, PixelColour{0x55}, PixelColour{0x53}, PixelColour{0xbf}, PixelColour{0xbd},
0xba, 0xb9, 0xb7, 0xb5 PixelColour{0xba}, PixelColour{0xb9}, PixelColour{0xb7}, PixelColour{0xb5}
}, },
{ {
0x0f, 0xd1, 0xd0, 0x57, PixelColour{0x0f}, PixelColour{0xd1}, PixelColour{0xd0}, PixelColour{0x57},
0x55, 0x53, 0x96, 0x95, PixelColour{0x55}, PixelColour{0x53}, PixelColour{0x96}, PixelColour{0x95},
0x94, 0x93, 0x92, 0x91 PixelColour{0x94}, PixelColour{0x93}, PixelColour{0x92}, PixelColour{0x91}
}, },
{ {
0x0f, 0x0b, 0x09, 0x07, PixelColour{0x0f}, PixelColour{0x0b}, PixelColour{0x09}, PixelColour{0x07},
0x05, 0x03, 0xbf, 0xbd, PixelColour{0x05}, PixelColour{0x03}, PixelColour{0xbf}, PixelColour{0xbd},
0xba, 0xb9, 0xb7, 0xb5 PixelColour{0xba}, PixelColour{0xb9}, PixelColour{0xb7}, PixelColour{0xb5}
}, },
{ {
0x0f, 0x0b, 0x0a, 0x09, PixelColour{0x0f}, PixelColour{0x0b}, PixelColour{0x0a}, PixelColour{0x09},
0x08, 0x07, 0x06, 0x05, PixelColour{0x08}, PixelColour{0x07}, PixelColour{0x06}, PixelColour{0x05},
0x04, 0x03, 0x02, 0x01 PixelColour{0x04}, PixelColour{0x03}, PixelColour{0x02}, PixelColour{0x01}
} }
}; };
@ -297,7 +297,7 @@ void LinkGraphOverlay::DrawLinks(const DrawPixelInfo *dpi) const
void LinkGraphOverlay::DrawContent(Point pta, Point ptb, const LinkProperties &cargo) const void LinkGraphOverlay::DrawContent(Point pta, Point ptb, const LinkProperties &cargo) const
{ {
uint usage_or_plan = std::min(cargo.capacity * 2 + 1, cargo.Usage()); uint usage_or_plan = std::min(cargo.capacity * 2 + 1, cargo.Usage());
int colour = LinkGraphOverlay::LINK_COLOURS[_settings_client.gui.linkgraph_colours][usage_or_plan * lengthof(LinkGraphOverlay::LINK_COLOURS[0]) / (cargo.capacity * 2 + 2)]; PixelColour colour = LinkGraphOverlay::LINK_COLOURS[_settings_client.gui.linkgraph_colours][usage_or_plan * lengthof(LinkGraphOverlay::LINK_COLOURS[0]) / (cargo.capacity * 2 + 2)];
int width = ScaleGUITrad(this->scale); int width = ScaleGUITrad(this->scale);
int dash = cargo.shared ? width * 4 : 0; int dash = cargo.shared ? width * 4 : 0;
@ -345,7 +345,7 @@ void LinkGraphOverlay::DrawStationDots(const DrawPixelInfo *dpi) const
* @param colour Colour with which the vertex will be filled. * @param colour Colour with which the vertex will be filled.
* @param border_colour Colour for the border of the vertex. * @param border_colour Colour for the border of the vertex.
*/ */
/* static */ void LinkGraphOverlay::DrawVertex(int x, int y, int size, int colour, int border_colour) /* static */ void LinkGraphOverlay::DrawVertex(int x, int y, int size, PixelColour colour, PixelColour border_colour)
{ {
size--; size--;
int w1 = size / 2; int w1 = size / 2;
@ -607,7 +607,7 @@ void LinkGraphLegendWindow::DrawWidget(const Rect &r, WidgetID widget) const
DrawCompanyIcon(cid, CentreBounds(br.left, br.right, sprite_size.width), CentreBounds(br.top, br.bottom, sprite_size.height)); DrawCompanyIcon(cid, CentreBounds(br.left, br.right, sprite_size.width), CentreBounds(br.top, br.bottom, sprite_size.height));
} }
if (IsInsideMM(widget, WID_LGL_SATURATION_FIRST, WID_LGL_SATURATION_LAST + 1)) { if (IsInsideMM(widget, WID_LGL_SATURATION_FIRST, WID_LGL_SATURATION_LAST + 1)) {
uint8_t colour = LinkGraphOverlay::LINK_COLOURS[_settings_client.gui.linkgraph_colours][widget - WID_LGL_SATURATION_FIRST]; PixelColour colour = LinkGraphOverlay::LINK_COLOURS[_settings_client.gui.linkgraph_colours][widget - WID_LGL_SATURATION_FIRST];
GfxFillRect(br, colour); GfxFillRect(br, colour);
StringID str = STR_NULL; StringID str = STR_NULL;
if (widget == WID_LGL_SATURATION_FIRST) { if (widget == WID_LGL_SATURATION_FIRST) {

View File

@ -44,7 +44,7 @@ public:
typedef std::map<StationID, StationLinkMap> LinkMap; typedef std::map<StationID, StationLinkMap> LinkMap;
typedef std::vector<std::pair<StationID, uint> > StationSupplyList; typedef std::vector<std::pair<StationID, uint> > StationSupplyList;
static const uint8_t LINK_COLOURS[][12]; static const PixelColour LINK_COLOURS[][12];
/** /**
* Create a link graph overlay for the specified window. * Create a link graph overlay for the specified window.
@ -95,7 +95,7 @@ protected:
void RebuildCache(); void RebuildCache();
static void AddStats(CargoType new_cargo, uint new_cap, uint new_usg, uint new_flow, uint32_t time, bool new_shared, LinkProperties &cargo); static void AddStats(CargoType new_cargo, uint new_cap, uint new_usg, uint new_flow, uint32_t time, bool new_shared, LinkProperties &cargo);
static void DrawVertex(int x, int y, int size, int colour, int border_colour); static void DrawVertex(int x, int y, int size, PixelColour colour, PixelColour border_colour);
}; };
void ShowLinkGraphLegend(); void ShowLinkGraphLegend();

View File

@ -42,13 +42,13 @@ LinkGraphJob::LinkGraphJob(const LinkGraph &orig) :
} }
/** /**
* Erase all flows originating at a specific node. * Erase all flows originating at a specific station.
* @param from Node to erase flows for. * @param from StationID to erase flows for.
*/ */
void LinkGraphJob::EraseFlows(NodeID from) void LinkGraphJob::EraseFlows(StationID from)
{ {
for (NodeID node_id = 0; node_id < this->Size(); ++node_id) { for (NodeID node_id = 0; node_id < this->Size(); ++node_id) {
(*this)[node_id].flows.erase(StationID{from}); (*this)[node_id].flows.erase(from);
} }
} }
@ -106,7 +106,7 @@ LinkGraphJob::~LinkGraphJob()
/* The station can have been deleted. Remove all flows originating from it then. */ /* The station can have been deleted. Remove all flows originating from it then. */
Station *st = Station::GetIfValid(from.base.station); Station *st = Station::GetIfValid(from.base.station);
if (st == nullptr) { if (st == nullptr) {
this->EraseFlows(node_id); this->EraseFlows(from.base.station);
continue; continue;
} }
@ -114,7 +114,7 @@ LinkGraphJob::~LinkGraphJob()
* sure that everything is still consistent or ignore it otherwise. */ * sure that everything is still consistent or ignore it otherwise. */
GoodsEntry &ge = st->goods[this->Cargo()]; GoodsEntry &ge = st->goods[this->Cargo()];
if (ge.link_graph != this->link_graph.index || ge.node != node_id) { if (ge.link_graph != this->link_graph.index || ge.node != node_id) {
this->EraseFlows(node_id); this->EraseFlows(from.base.station);
continue; continue;
} }
@ -136,7 +136,7 @@ LinkGraphJob::~LinkGraphJob()
/* Delete old flows for source stations which have been deleted /* Delete old flows for source stations which have been deleted
* from the new flows. This avoids flow cycles between old and * from the new flows. This avoids flow cycles between old and
* new flows. */ * new flows. */
while (!erased.IsEmpty()) geflows.erase(StationID{erased.Pop()}); while (!erased.IsEmpty()) geflows.erase(erased.Pop());
} else if ((*lg)[node_id][dest_id].last_unrestricted_update == EconomyTime::INVALID_DATE) { } else if ((*lg)[node_id][dest_id].last_unrestricted_update == EconomyTime::INVALID_DATE) {
/* Edge is fully restricted. */ /* Edge is fully restricted. */
flows.RestrictFlows(to); flows.RestrictFlows(to);

View File

@ -167,7 +167,7 @@ protected:
std::atomic<bool> job_completed = false; ///< Is the job still running. This is accessed by multiple threads and reads may be stale. std::atomic<bool> job_completed = false; ///< Is the job still running. This is accessed by multiple threads and reads may be stale.
std::atomic<bool> job_aborted = false; ///< Has the job been aborted. This is accessed by multiple threads and reads may be stale. std::atomic<bool> job_aborted = false; ///< Has the job been aborted. This is accessed by multiple threads and reads may be stale.
void EraseFlows(NodeID from); void EraseFlows(StationID from);
void JoinThread(); void JoinThread();
void SpawnThread(); void SpawnThread();

View File

@ -556,7 +556,7 @@ void SetupColoursAndInitialWindow()
const uint8_t *b = GetNonSprite(GetColourPalette(i), SpriteType::Recolour) + 1; const uint8_t *b = GetNonSprite(GetColourPalette(i), SpriteType::Recolour) + 1;
assert(b != nullptr); assert(b != nullptr);
for (ColourShade j = SHADE_BEGIN; j < SHADE_END; j++) { for (ColourShade j = SHADE_BEGIN; j < SHADE_END; j++) {
SetColourGradient(i, j, b[0xC6 + j]); SetColourGradient(i, j, PixelColour{b[0xC6 + j]});
} }
} }

View File

@ -37,6 +37,12 @@ if(NOT OPTION_DEDICATED)
CONDITION HAIKU CONDITION HAIKU
) )
add_files(
alsamidi.cpp
alsamidi.h
CONDITION ALSA_FOUND
)
add_files( add_files(
midi.h midi.h
midifile.cpp midifile.cpp

View File

@ -0,0 +1,604 @@
/*
* This file is part of OpenTTD.
* OpenTTD 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, version 2.
* OpenTTD 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 OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file alsamidi.cpp Support for ALSA Linux MIDI. */
#include "../stdafx.h"
#include "../openttd.h"
#include "alsamidi.h"
#include "../base_media_base.h"
#include "midifile.hpp"
#include "../debug.h"
#include "midi.h"
#include <chrono>
#include <thread>
#include "../safeguards.h"
/** Factory for ALSA MIDI player. */
static FMusicDriver_AlsaMidi iFMusicDriver_AlsaMidi;
std::optional<std::string_view> MusicDriver_AlsaMidi::Start(const StringList &parm)
{
this->playing.store(false);
Debug(driver, 2, "ALSA MIDI: Start");
this->dev_port = (uint)GetDriverParamInt(parm, "port", UINT_MAX);
Debug(driver, 2, "ALSA MIDI: using MIDI device at port {}", dev_port);
// Open sequencer
if (snd_seq_open(&this->seq, "default", SND_SEQ_OPEN_OUTPUT, 0) < 0) {
return "Failed to open ALSA sequencer";
}
snd_seq_set_client_name(this->seq, "OpenTTD MIDI Out");
// Create port
this->seq_port = snd_seq_create_simple_port(this->seq, "MIDI Out",
SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ,
SND_SEQ_PORT_TYPE_MIDI_GENERIC|SND_SEQ_PORT_TYPE_APPLICATION);
if (this->seq_port < 0) {
return "Failed to create ALSA sequencer port";
}
// Create event queue
Debug(driver, 2, "ALSA MIDI: Creating sequencer event queue");
this->queue_id = snd_seq_alloc_named_queue(seq, "OpenTTD Sequencer Queue");
if (this->queue_id < 0) {
return "Failed to create ALSA sequencer event queue";
}
// Set a slightly larger event output buffer than normal
snd_seq_set_client_pool_output(this->seq, 1000);
snd_seq_set_client_pool_output_room(this->seq, 1000);
snd_seq_set_output_buffer_size(this->seq, 1000);
// Turn on nonblocking mode
snd_seq_nonblock(this->seq, 1);
snd_seq_addr_t sender, dest;
// Setup event port
sender.client = snd_seq_client_id(this->seq);
sender.port = this->seq_port;
dest.client = this->dev_port;
dest.port = this->seq_port;
snd_seq_port_subscribe_t* subs;
snd_seq_port_subscribe_alloca(&subs);
snd_seq_port_subscribe_set_sender(subs, &sender);
snd_seq_port_subscribe_set_dest(subs, &dest);
if (snd_seq_subscribe_port(this->seq, subs) < 0) {
return "Failed to connect to port";
}
Debug(driver, 2, "ALSA MIDI: opened sequencer port {}", this->seq_port);
this->InitMidiVolume();
return std::nullopt;
}
void MusicDriver_AlsaMidi::Stop()
{
Debug(driver, 2, "ALSA MIDI: stopping");
this->StopSong();
if (this->queue_id) {
Debug(driver, 2, "ALSA MIDI: freeing sequencer event queue");
snd_seq_free_queue(this->seq, this->queue_id);
}
if (this->seq) {
Debug(driver, 2, "ALSA MIDI: closing sequencer handle");
snd_seq_close(this->seq);
}
}
void MusicDriver_AlsaMidi::PlaySong(const MusicSongInfo &song)
{
Debug(driver, 2, "ALSA MIDI: PlaySong");
std::string filename = MidiFile::GetSMFFile(song);
smf::MidiFile midifile;
Debug(driver, 2, "ALSA MIDI: reading SMFFile");
if (!filename.empty()) {
if (!midifile.read(filename)) {
Debug(driver, 2, "ALSA MIDI: error reading SMFFile");
}
}
// Sort
midifile.sortTracks();
// Convert MIDI ticks to absolute seconds
midifile.doTimeAnalysis();
// Merge > 1 tracks into single track for easier queueing.
// (WriteSMF only creates single-track MIDIs, other packs may be multitrack)
midifile.joinTracks();
if (this->playing.load() == true) {
this->StopSong();
}
Debug(driver, 2, "ALSA MIDI: starting playback of {}", song.songname);
Debug(driver, 2, "ALSA MIDI: SMF filename {}", filename);
// ALSA does not allow setting PPQ on started queues, so do this first.
// Tempo may be adjusted later, on a started/running queue.
int ppq = midifile.getTPQ();
this->SetPPQ(ppq);
this->SetupPolling();
snd_seq_start_queue(this->seq, this->queue_id, nullptr);
snd_seq_drain_output(this->seq);
this->playing.store(true);
StartNewThread(&this->_queue_thread, "ottd:alsamidi", &StartQueue, this, std::move(midifile));
}
void MusicDriver_AlsaMidi::SetupPolling()
{
int poll_fd_cnt = snd_seq_poll_descriptors_count(this->seq, POLLOUT);
this->poll_fds.resize(poll_fd_cnt);
snd_seq_poll_descriptors(this->seq, this->poll_fds.data(), poll_fd_cnt, POLLOUT);
}
/**
* Starts the ALSA sequencer queue, iterates through the MIDI events in the file,
* converts them to ALSA sequencer events, and pushes them onto the queue.
*
* This function is blocking and expects to be run in a thread. It will block
* until either it is signaled to stop (in which case it will purge the ALSA queue,
* send a GM RESET, and terminate), or it has enqueued all events in the MIDI file,
* and waited for the queue to finish processing them all.
*
* @param drv Pointer to `this` instance of the class
* @param midifile The previously-loaded, sorted, and time-corrected STD MIDI file.
*
* @see Stopping()
* @see StopSong()
* @see WaitForFinish()
*/
static void StartQueue(MusicDriver_AlsaMidi *drv, const smf::MidiFile midifile)
{
Debug(driver, 2, "ALSA MIDI: queue thread started");
unsigned int last_tick;
// Push all events for all tracks to the sequencer queue
for (int track = 0; track < midifile.getNumTracks(); track++) {
std::vector<uint8_t> sysex_buffer;
for (int event = 0; event < midifile[track].size(); event++) {
auto& ev = midifile[track][event];
last_tick = static_cast<unsigned int>(ev.tick);
if (drv->Stopping()) {
Debug(driver, 2, "ALSA MIDI: Looks like we are stopping, bailing out of queue thread");
drv->SendResetEvent();
drv->StopQueue();
sysex_buffer.clear();
return;
}
if (ev.isTempo()) {
// Handle tempo change here, as we have to change it for the whole queue
Debug(driver, 2, "ALSA MIDI: Got tempo change event in queue thread");
int tempo_uspq = ev.getTempoMicroseconds();
drv->UpdateTempo(tempo_uspq);
continue;
}
// Handle SYSEX events
// SYSEX events may
// 1. Be a complete SYSEX event (begin with F0 and end with F7)
// 2. Be a "middle" SYSEX event (a previous message began with F0)
// 3. Be an "end" SYSEX event (a previous message began with F0, and this one ends with F7)
// This basically means you need an accumulator. Split SYSEX messages are *rare* but exist.
if (ev.getCommandByte() == MIDIST_SYSEX) {
Debug(driver, 2, "ALSA MIDI: got SYSEX message");
sysex_buffer.clear();
// If this is is a complete (not partial) SYSEX message, send it
// Otherwise, accumulate it as a partial and continue to the next
if (ev.back() == MIDIST_ENDSYSEX) {
Debug(driver, 2, "ALSA MIDI: complete SYSEX, sending");
sysex_buffer.insert(sysex_buffer.end(), ev.begin() + 1, ev.end() -1);
drv->SendSysexEvent(sysex_buffer);
sysex_buffer.clear();
} else {
sysex_buffer.insert(sysex_buffer.end(), ev.begin() + 1, ev.end());
}
continue;
}
if (!sysex_buffer.empty() && ev.back() == MIDIST_ENDSYSEX) {
Debug(driver, 2, "ALSA MIDI: partial SYSEX completed, sending");
sysex_buffer.insert(sysex_buffer.end(), ev.begin(), ev.end() -1);
drv->SendSysexEvent(sysex_buffer);
sysex_buffer.clear();
continue;
}
if (!sysex_buffer.empty()) {
Debug(driver, 2, "ALSA MIDI: partial SYSEX continuing");
sysex_buffer.insert(sysex_buffer.end(), ev.begin(), ev.end());
continue;
}
// At this point, it's just a regular event - handle it.
drv->SendEvent(ev);
}
}
Debug(driver, 2, "ALSA MIDI: queue thread finished, waiting for events");
drv->WaitForFinish(last_tick);
drv->StopQueue();
}
/**
* Stops the ALSA sequencer queue, and sets `playing` to false.
*
* Note that this does not clear or drop any pending events in the queue
* before stopping it.
*
* @see SendResetEvent()
*/
void MusicDriver_AlsaMidi::StopQueue()
{
Debug(driver, 2, "ALSA MIDI: stopping current queue!");
snd_seq_stop_queue(this->seq, this->queue_id, nullptr);
snd_seq_drain_output(this->seq);
this->poll_fds.clear();
this->playing.store(false);
}
/**
* Sends a SYSEX GM reset message, after dropping all pending events in the
* queue.
*
* Does not stop the queue.
*
* @see StopQueue()
*/
void MusicDriver_AlsaMidi::SendResetEvent()
{
// Drop anything still in the queue, this is a disruptive reset.
snd_seq_drop_output(this->seq);
std::vector<unsigned char> sysex_rst_msg = {0x7E, 0x7F, 0x09, 0x01};
this->SendSysexEvent(sysex_rst_msg);
}
/**
* Generic helper for sending SYSEX messages.
*
* Note that this sends all SYSEX messages as "direct"/unscheduled events
* (skips tick queue).
*
* @param data The SYSEX message data (excluding 0xF0/0xF7 begin/end markers).
*
* @see SendEvent()
*/
void MusicDriver_AlsaMidi::SendSysexEvent(const std::vector<uint8_t> data)
{
snd_seq_event_t seqev;
snd_seq_ev_clear(&seqev);
snd_seq_ev_set_source(&seqev, this->seq_port);
snd_seq_ev_set_subs(&seqev);
std::vector<uint8_t> complete_message;
complete_message.reserve(data.size() + 2);
complete_message.push_back(0xF0); // Start of SysEx
complete_message.insert(complete_message.end(), data.begin(), data.end());
complete_message.push_back(0xF7); // End of SysEx
snd_seq_ev_set_sysex(&seqev, complete_message.size(), complete_message.data());
// TODO this assumes all SYSEX msgs are immediate, and not queued with a tick
// this is correct for SYSEX GM RESET (all this is currently used for) but
// might not be globally correct.
snd_seq_ev_set_direct(&seqev);
this->PushEvent(seqev);
}
/**
* Generic helper for sending non-SYSEX messages.
*
* Converts MIDI events from the file into ALSA-specific sequencer queue events,
* and schedules them on the tick-based sequencer queue.
*
* @param ev The raw MIDI event from the file.
*
* @see SendSysexEvent()
*/
void MusicDriver_AlsaMidi::SendEvent(const smf::MidiEvent& ev)
{
snd_seq_event_t seqev;
snd_seq_ev_clear(&seqev);
unsigned int ticks = static_cast<unsigned int>(ev.tick);
if (ev.isNoteOn()) {
snd_seq_ev_set_noteon(&seqev, ev.getChannel(), ev[1], ev[2]);
} else if (ev.isNoteOff()) {
snd_seq_ev_set_noteoff(&seqev, ev.getChannel(), ev[1], ev[2]);
} else if (ev.isController()) {
if (ev[1] == MIDI_CTL_MSB_MAIN_VOLUME) {
this->UpdateChannelVolume(ev.getChannel(), ev[2]);
snd_seq_ev_set_controller(&seqev, ev.getChannel(), ev[1], this->vol_state.current_volume[ev.getChannel()]);
}
snd_seq_ev_set_controller(&seqev, ev.getChannel(), ev[1], ev[2]);
} else if (ev.isPatchChange()) {
snd_seq_ev_set_pgmchange(&seqev, ev.getChannel(), ev[1]);
} else if (ev.isPitchbend()) {
snd_seq_ev_set_pitchbend(&seqev, ev.getChannel(), ((ev[2] << 7) | ev[1]) - 8192);
} else if (ev.isPressure()) {
snd_seq_ev_set_chanpress(&seqev, ev.getChannel(), ev[1]);
} else if (ev.isAftertouch()) {
snd_seq_ev_set_keypress(&seqev, ev.getChannel(), ev[1], ev[2]);
} else if (ev.getCommandNibble() == 0xF0 && ev.getCommandByte() == MIDIST_SYSRESET) {
Debug(driver, 2, "ALSA MIDI: reset event");
snd_seq_ev_set_fixed(&seqev);
seqev.type = SND_SEQ_EVENT_RESET;
} else if (ev.isMeta()) {
Debug(driver, 2, "ALSA MIDI: ignoring meta message");
return;
} else {
Debug(driver, 2, "ALSA MIDI: unknown message: {}", ev.getCommandNibble());
return;
}
// Schedule event
snd_seq_ev_schedule_tick(&seqev, this->queue_id, 0, ticks);
snd_seq_ev_set_source(&seqev, this->seq_port);
snd_seq_ev_set_subs(&seqev);
this->PushEvent(seqev);
}
/**
* Waits until either:
*
* 1. The ALSA sequencer finishes processing up to the last event
* that was enqueued, as measured by comparing the tick value of the
* last event against the current tick value of the ALSA queue state.
*
* 2. `Stopping()` returns true, signaling early exit (don't wait for playback to finish).
*
* @param last_event_tick Tick value of the last event that was added to the queue.
*
* @see Stopping()
*/
void MusicDriver_AlsaMidi::WaitForFinish(const unsigned int last_event_tick)
{
Debug(driver, 2, "ALSA MIDI: waiting for events finish");
// First wait for queue to drain
int res = 0;
do {
res = snd_seq_drain_output(this->seq);
if (res != 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
} while (res != 0);
// Now get queue status and wait until we've passed the last scheduled tick
snd_seq_queue_status_t *status;
snd_seq_queue_status_alloca(&status);
do {
snd_seq_get_queue_status(this->seq, this->queue_id, status);
const snd_seq_tick_time_t current_tick = snd_seq_queue_status_get_tick_time(status);
if (this->Stopping()) {
Debug(driver, 2, "ALSA MIDI: got stop signal, not waiting for events to finish");
this->SendResetEvent();
break;
}
if (current_tick >= last_event_tick) {
// This is necessarily imprecise, just because the queue has processed the last
// tick event doesn't mean whatever output device in use has played it yet,
// but in practice this is good enough to not cut off the last few notes.
std::this_thread::sleep_for(std::chrono::milliseconds(500));
break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(10));
} while(true);
Debug(driver, 2, "ALSA MIDI: events finished");
}
/**
* Pushes an ALSA sequencer event onto the ALSA sequencer queue.
*
* If the push fails because the output buffer is full, uses `poll()`
* to wait until there's space/device is ready.
*
* @param seqev ALSA sequencer event to push.
*/
void MusicDriver_AlsaMidi::PushEvent(snd_seq_event_t seqev)
{
// Wait for space in the queue via `poll()`
while (snd_seq_event_output_direct(this->seq, &seqev) < 0) {
poll(this->poll_fds.data(), this->poll_fds.size(), 100); // 100ms timeout
}
}
/**
* Signals the queue thread to terminate and joins it.
*/
void MusicDriver_AlsaMidi::StopSong()
{
this->stopping.store(true);
Debug(driver, 2, "ALSA MIDI: StopSong waiting for queue thread");
if (this->_queue_thread.joinable()) {
this->_queue_thread.join();
}
Debug(driver, 2, "ALSA MIDI: stopping current queue");
this->stopping.store(false);
Debug(driver, 2, "ALSA MIDI: stopped song");
}
bool MusicDriver_AlsaMidi::Stopping()
{
return this->stopping.load();
}
bool MusicDriver_AlsaMidi::IsSongPlaying()
{
return this->playing.load();
}
/**
* Sets the desired volume for the MIDI sequencer (from the UI thread)
*
* Note that this implementation will internally debounce rapid subsequent calls to
* SetVolume(), to avoid overwhelming the sequencer and its queues and buffers with
* incremental volume updates. The magnitude of the volume change is taken into account
* for this.
*
*/
void MusicDriver_AlsaMidi::SetVolume(uint8_t vol)
{
Debug(driver, 2, "ALSA MIDI: got volume level update {}", vol);
// Adaptive debounce: small changes need more time between updates
auto now = std::chrono::steady_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - this->last_volume_update);
int current_vol = this->current_vol.load();
int change_magnitude = std::abs(vol - current_vol);
int required_ms = change_magnitude < 5 ? SMALL_VOL_DEBOUNCE :
change_magnitude < 15 ? MED_VOL_DEBOUNCE :
LARGE_VOL_DEBOUNCE;
if (elapsed.count() >= required_ms || current_vol == 127) {
Debug(driver, 2, "ALSA MIDI: got volume level update {}", vol);
if (vol != current_vol) {
this->SetScaledVolume(vol);
this->last_volume_update = now;
}
}
}
/**
* Initializes the volume state for the player.
*
* @see UpdateChannelVolume()
* @see SetScaledVolume()
*/
void MusicDriver_AlsaMidi::InitMidiVolume()
{
this->last_volume_update = std::chrono::steady_clock::now();
this->vol_state.master_scale = 127;
for (int i = 0; i < MIDI_CHANNELS; i++) {
this->vol_state.base_volume[i] = 127;
this->vol_state.current_volume[i] = 127;
}
}
/**
* Scales current volume level for each channel according to the scale
* factor provided. This is to maintain the relative volume levels between
* channels as set by the midi file, while scaling up or down.
*
* @param channel to update the volume for.
* @param value requested volume level to scale selected channel volume against.
*/
void MusicDriver_AlsaMidi::UpdateChannelVolume(int channel, int value)
{
std::lock_guard<std::mutex> lock(this->vol_state.mutex);
this->vol_state.base_volume[channel] = value;
this->vol_state.current_volume[channel] = (value * this->vol_state.master_scale) / 127;
Debug(driver, 2, "ALSA MIDI: upading volume for channel {} to {}, base: {}, scale {}", channel, this->vol_state.current_volume[channel], this->vol_state.base_volume[channel], this->vol_state.master_scale);
}
/**
* Scales current volume level for each channel according to the scale
* factor provided. This is to maintain the relative volume levels between
* channels as set by the midi file, while scaling up or down.
*
* @param value requested volume level (0-127) to scale all channel volume levels against.
*/
void MusicDriver_AlsaMidi::SetScaledVolume(int value)
{
std::lock_guard<std::mutex> lock(this->vol_state.mutex);
this->vol_state.master_scale = (value > 127) ? 127 : (value < 0) ? 0 : value;
for (int i = 0; i < MIDI_CHANNELS; i++) {
this->vol_state.current_volume[i] = ( this->vol_state.base_volume[i] * this->vol_state.master_scale) / 127;
snd_seq_event_t vol_ev;
snd_seq_ev_clear(&vol_ev);
Debug(driver, 2, "ALSA MIDI: setting volume for channel {} to {}, master: {} base: {}", i, this->vol_state.current_volume[i], this->vol_state.master_scale, this->vol_state.base_volume[i]);
snd_seq_ev_set_controller(&vol_ev, i, MIDI_CTL_MSB_MAIN_VOLUME, this->vol_state.current_volume[i]);
snd_seq_ev_set_source(&vol_ev, this->seq_port);
snd_seq_ev_set_subs(&vol_ev);
snd_seq_ev_set_direct(&vol_ev);
this->PushEvent(vol_ev);
}
this->current_vol.store(value);
}
/**
* Updates the tempo of the current (started) ALSA sequencer queue.
*
* @param tempo_uspq Tempo value in units per quarter note.
*/
void MusicDriver_AlsaMidi::UpdateTempo(const int tempo_uspq)
{
if (snd_seq_change_queue_tempo(this->seq, this->queue_id, tempo_uspq, nullptr) < 0) {
throw std::runtime_error("Failed to update queue tempo");
}
snd_seq_drain_output(this->seq);
}
/**
* Updates the Pulses Per Quarternote (PPQ) of the current ALSA sequencer queue.
*
* Note that the PPQ of an ALSA sequencer queue cannot be changed after it is started.
*
* @param ppq Pulse per quarter note value.
*
* @see StopQueue()
*/
void MusicDriver_AlsaMidi::SetPPQ(const int ppq)
{
Debug(driver, 2, "ALSA MIDI: setting PPQ to {}", ppq);
snd_seq_queue_tempo_t* tempo;
snd_seq_queue_tempo_alloca(&tempo);
snd_seq_queue_tempo_set_ppq(tempo, ppq);
snd_seq_queue_tempo_set_tempo(tempo, 1000000); // 60 BPM
snd_seq_queue_status_t *status;
snd_seq_queue_status_alloca(&status);
snd_seq_get_queue_status(seq, queue_id, status);
if (snd_seq_queue_status_get_status(status) == 0) {
if (snd_seq_set_queue_tempo(this->seq, this->queue_id, tempo) < 0) {
throw std::runtime_error("Failed to set queue PPQ");
}
snd_seq_drain_output(this->seq);
} else {
Debug(driver, 2, "ALSA MIDI: tried to set PPQ on non-stopped queue!");
}
}

View File

@ -0,0 +1,93 @@
/*
* This file is part of OpenTTD.
* OpenTTD 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, version 2.
* OpenTTD 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 OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file alsamidi.h Base of Alsa MIDI support. */
#ifndef MUSIC_ALSAMIDI_H
#define MUSIC_ALSAMIDI_H
#include "music_driver.hpp"
#include <alsa/asoundlib.h>
#include "../3rdparty/midifile/MidiFile.h"
#include "../../thread.h"
#include <atomic>
const int SMALL_VOL_DEBOUNCE = 200; // Tiny changes: 200ms
const int MED_VOL_DEBOUNCE = 50; // Small changes: 50ms
const int LARGE_VOL_DEBOUNCE = 10; // Large changes: 10ms
/** The midi player for ALSA on Linux. */
class MusicDriver_AlsaMidi : public MusicDriver {
public:
std::optional<std::string_view> Start(const StringList &param) override;
void Stop() override;
void PlaySong(const MusicSongInfo &song) override;
void StopSong() override;
bool IsSongPlaying() override;
void SetVolume(uint8_t vol) override;
std::string_view GetName() const override { return "alsamidi"; }
void WaitForFinish(const unsigned int last_tick);
void StopQueue();
void SendEvent(const smf::MidiEvent& event);
void SendSysexEvent(const std::vector<uint8_t> data);
void SendResetEvent();
void UpdateTempo(const int tempo_uspq);
bool Stopping();
private:
typedef struct
{
int base_volume[MIDI_CHANNELS];
int master_scale;
int current_volume[MIDI_CHANNELS];
std::mutex mutex;
}MidiVolume;
MidiVolume vol_state;
snd_seq_t* seq;
int queue_id;
int seq_port;
int dev_port;
std::vector<struct pollfd> poll_fds;
std::thread _queue_thread;
std::atomic<bool> playing{false};
std::atomic<bool> stopping{false};
std::atomic<uint8_t> current_vol{127};
std::chrono::time_point<std::chrono::steady_clock> last_volume_update;
void SetPPQ(const int ppq);
void SetScaledVolume(int value);
void SetupPolling();
void PushEvent(snd_seq_event_t seqev);
void InitMidiVolume();
void UpdateChannelVolume(int channel, int value);
void VolumeAdjust (const uint8_t new_vol);
};
/** Factory for the Linux ALSA midi player. */
class FMusicDriver_AlsaMidi : public DriverFactoryBase {
public:
FMusicDriver_AlsaMidi() : DriverFactoryBase(Driver::DT_MUSIC, 10, "alsamidi", "ALSA Linux MIDI Driver") {}
Driver *CreateInstance() const override { return new MusicDriver_AlsaMidi(); }
};
static void StartQueue(MusicDriver_AlsaMidi *drv, const smf::MidiFile midifile);
#endif /* MUSIC_ALSAMIDI_H */

View File

@ -97,11 +97,11 @@ static ChangeInfoResult CargoReserveInfo(uint first, uint last, int prop, ByteRe
break; break;
case 0x13: // Colour for station rating bars case 0x13: // Colour for station rating bars
cs->rating_colour = buf.ReadByte(); cs->rating_colour = PixelColour{buf.ReadByte()};
break; break;
case 0x14: // Colour for cargo graph case 0x14: // Colour for cargo graph
cs->legend_colour = buf.ReadByte(); cs->legend_colour = PixelColour{buf.ReadByte()};
break; break;
case 0x15: // Freight status case 0x15: // Freight status

View File

@ -565,7 +565,7 @@ static ChangeInfoResult IndustriesChangeInfo(uint first, uint last, int prop, By
break; break;
case 0x19: // Map colour case 0x19: // Map colour
indsp->map_colour = buf.ReadByte(); indsp->map_colour = PixelColour{buf.ReadByte()};
break; break;
case 0x1A: // Special industry flags to define special behavior case 0x1A: // Special industry flags to define special behavior

View File

@ -121,7 +121,7 @@ static ChangeInfoResult RailTypeChangeInfo(uint first, uint last, int prop, Byte
break; break;
case 0x16: // Map colour case 0x16: // Map colour
rti->map_colour = buf.ReadByte(); rti->map_colour = PixelColour{buf.ReadByte()};
break; break;
case 0x17: // Introduction date case 0x17: // Introduction date

View File

@ -109,7 +109,7 @@ static ChangeInfoResult RoadTypeChangeInfo(uint first, uint last, int prop, Byte
break; break;
case 0x16: // Map colour case 0x16: // Map colour
rti->map_colour = buf.ReadByte(); rti->map_colour = PixelColour{buf.ReadByte()};
break; break;
case 0x17: // Introduction date case 0x17: // Introduction date

View File

@ -368,7 +368,7 @@ StationIDStack OrderList::GetNextStoppingStation(const Vehicle *v, VehicleOrderI
next = v->cur_implicit_order_index; next = v->cur_implicit_order_index;
if (next >= this->GetNumOrders()) { if (next >= this->GetNumOrders()) {
next = this->GetFirstOrder(); next = this->GetFirstOrder();
if (next == INVALID_VEH_ORDER_ID) return StationID::Invalid().base(); if (next == INVALID_VEH_ORDER_ID) return StationID::Invalid();
} else { } else {
/* GetNext never returns INVALID_VEH_ORDER_ID if there is a valid station in the list. /* GetNext never returns INVALID_VEH_ORDER_ID if there is a valid station in the list.
* As the given "next" is already valid and a station in the list, we * As the given "next" is already valid and a station in the list, we
@ -404,11 +404,11 @@ StationIDStack OrderList::GetNextStoppingStation(const Vehicle *v, VehicleOrderI
if (next == INVALID_VEH_ORDER_ID || ((orders[next].IsType(OT_GOTO_STATION) || orders[next].IsType(OT_IMPLICIT)) && if (next == INVALID_VEH_ORDER_ID || ((orders[next].IsType(OT_GOTO_STATION) || orders[next].IsType(OT_IMPLICIT)) &&
orders[next].GetDestination() == v->last_station_visited && orders[next].GetDestination() == v->last_station_visited &&
(orders[next].GetUnloadType() & (OUFB_TRANSFER | OUFB_UNLOAD)) != 0)) { (orders[next].GetUnloadType() & (OUFB_TRANSFER | OUFB_UNLOAD)) != 0)) {
return StationID::Invalid().base(); return StationID::Invalid();
} }
} while (orders[next].IsType(OT_GOTO_DEPOT) || orders[next].GetDestination() == v->last_station_visited); } while (orders[next].IsType(OT_GOTO_DEPOT) || orders[next].GetDestination() == v->last_station_visited);
return orders[next].GetDestination().ToStationID().base(); return orders[next].GetDestination().ToStationID();
} }
/** /**

View File

@ -358,9 +358,9 @@ void DoPaletteAnimations()
* @param threshold Background colour brightness threshold below which the background is considered dark and TC_WHITE is returned, range: 0 - 255, default 128. * @param threshold Background colour brightness threshold below which the background is considered dark and TC_WHITE is returned, range: 0 - 255, default 128.
* @return TC_BLACK or TC_WHITE depending on what gives a better contrast. * @return TC_BLACK or TC_WHITE depending on what gives a better contrast.
*/ */
TextColour GetContrastColour(uint8_t background, uint8_t threshold) TextColour GetContrastColour(PixelColour background, uint8_t threshold)
{ {
Colour c = _cur_palette.palette[background]; Colour c = _cur_palette.palette[background.p];
/* Compute brightness according to http://www.w3.org/TR/AERT#color-contrast. /* Compute brightness according to http://www.w3.org/TR/AERT#color-contrast.
* The following formula computes 1000 * brightness^2, with brightness being in range 0 to 255. */ * The following formula computes 1000 * brightness^2, with brightness being in range 0 to 255. */
uint sq1000_brightness = c.r * c.r * 299 + c.g * c.g * 587 + c.b * c.b * 114; uint sq1000_brightness = c.r * c.r * 299 + c.g * c.g * 587 + c.b * c.b * 114;
@ -374,7 +374,7 @@ TextColour GetContrastColour(uint8_t background, uint8_t threshold)
*/ */
struct ColourGradients struct ColourGradients
{ {
using ColourGradient = std::array<uint8_t, SHADE_END>; using ColourGradient = std::array<PixelColour, SHADE_END>;
static inline std::array<ColourGradient, COLOUR_END> gradient{}; static inline std::array<ColourGradient, COLOUR_END> gradient{};
}; };
@ -385,7 +385,7 @@ struct ColourGradients
* @param shade Shade level from 1 to 7. * @param shade Shade level from 1 to 7.
* @returns palette index of colour. * @returns palette index of colour.
*/ */
uint8_t GetColourGradient(Colours colour, ColourShade shade) PixelColour GetColourGradient(Colours colour, ColourShade shade)
{ {
return ColourGradients::gradient[colour % COLOUR_END][shade % SHADE_END]; return ColourGradients::gradient[colour % COLOUR_END][shade % SHADE_END];
} }
@ -396,7 +396,7 @@ uint8_t GetColourGradient(Colours colour, ColourShade shade)
* @param shade Shade level from 1 to 7. * @param shade Shade level from 1 to 7.
* @param palette_index Palette index to set. * @param palette_index Palette index to set.
*/ */
void SetColourGradient(Colours colour, ColourShade shade, uint8_t palette_index) void SetColourGradient(Colours colour, ColourShade shade, PixelColour palette_index)
{ {
assert(colour < COLOUR_END); assert(colour < COLOUR_END);
assert(shade < SHADE_END); assert(shade < SHADE_END);

View File

@ -67,7 +67,7 @@ inline bool IsValidColours(Colours colours)
return colours < COLOUR_END; return colours < COLOUR_END;
} }
TextColour GetContrastColour(uint8_t background, uint8_t threshold = 128); TextColour GetContrastColour(PixelColour background, uint8_t threshold = 128);
enum ColourShade : uint8_t { enum ColourShade : uint8_t {
SHADE_BEGIN = 0, SHADE_BEGIN = 0,
@ -83,45 +83,45 @@ enum ColourShade : uint8_t {
}; };
DECLARE_INCREMENT_DECREMENT_OPERATORS(ColourShade) DECLARE_INCREMENT_DECREMENT_OPERATORS(ColourShade)
uint8_t GetColourGradient(Colours colour, ColourShade shade); PixelColour GetColourGradient(Colours colour, ColourShade shade);
void SetColourGradient(Colours colour, ColourShade shade, uint8_t palette_colour); void SetColourGradient(Colours colour, ColourShade shade, PixelColour palette_colour);
/** /**
* Return the colour for a particular greyscale level. * Return the colour for a particular greyscale level.
* @param level Intensity, 0 = black, 15 = white * @param level Intensity, 0 = black, 15 = white
* @return colour * @return colour
*/ */
constexpr uint8_t GREY_SCALE(uint8_t level) { return level; } inline constexpr PixelColour GREY_SCALE(uint8_t level) { return PixelColour{level}; }
static const uint8_t PC_BLACK = GREY_SCALE(1); ///< Black palette colour. static constexpr PixelColour PC_BLACK {GREY_SCALE(1)}; ///< Black palette colour.
static const uint8_t PC_DARK_GREY = GREY_SCALE(6); ///< Dark grey palette colour. static constexpr PixelColour PC_DARK_GREY {GREY_SCALE(6)}; ///< Dark grey palette colour.
static const uint8_t PC_GREY = GREY_SCALE(10); ///< Grey palette colour. static constexpr PixelColour PC_GREY {GREY_SCALE(10)}; ///< Grey palette colour.
static const uint8_t PC_WHITE = GREY_SCALE(15); ///< White palette colour. static constexpr PixelColour PC_WHITE {GREY_SCALE(15)}; ///< White palette colour.
static const uint8_t PC_VERY_DARK_RED = 0xB2; ///< Almost-black red palette colour. static constexpr PixelColour PC_VERY_DARK_RED {0xB2}; ///< Almost-black red palette colour.
static const uint8_t PC_DARK_RED = 0xB4; ///< Dark red palette colour. static constexpr PixelColour PC_DARK_RED {0xB4}; ///< Dark red palette colour.
static const uint8_t PC_RED = 0xB8; ///< Red palette colour. static constexpr PixelColour PC_RED {0xB8}; ///< Red palette colour.
static const uint8_t PC_VERY_DARK_BROWN = 0x56; ///< Almost-black brown palette colour. static constexpr PixelColour PC_VERY_DARK_BROWN {0x56}; ///< Almost-black brown palette colour.
static const uint8_t PC_ORANGE = 0xC2; ///< Orange palette colour. static constexpr PixelColour PC_ORANGE {0xC2}; ///< Orange palette colour.
static const uint8_t PC_YELLOW = 0xBF; ///< Yellow palette colour. static constexpr PixelColour PC_YELLOW {0xBF}; ///< Yellow palette colour.
static const uint8_t PC_LIGHT_YELLOW = 0x44; ///< Light yellow palette colour. static constexpr PixelColour PC_LIGHT_YELLOW {0x44}; ///< Light yellow palette colour.
static const uint8_t PC_VERY_LIGHT_YELLOW = 0x45; ///< Almost-white yellow palette colour. static constexpr PixelColour PC_VERY_LIGHT_YELLOW {0x45}; ///< Almost-white yellow palette colour.
static const uint8_t PC_GREEN = 0xD0; ///< Green palette colour. static constexpr PixelColour PC_GREEN {0xD0}; ///< Green palette colour.
static const uint8_t PC_VERY_DARK_BLUE = 0x9A; ///< Almost-black blue palette colour. static constexpr PixelColour PC_VERY_DARK_BLUE {0x9A}; ///< Almost-black blue palette colour.
static const uint8_t PC_DARK_BLUE = 0x9D; ///< Dark blue palette colour. static constexpr PixelColour PC_DARK_BLUE {0x9D}; ///< Dark blue palette colour.
static const uint8_t PC_LIGHT_BLUE = 0x98; ///< Light blue palette colour. static constexpr PixelColour PC_LIGHT_BLUE {0x98}; ///< Light blue palette colour.
static const uint8_t PC_ROUGH_LAND = 0x52; ///< Dark green palette colour for rough land. static constexpr PixelColour PC_ROUGH_LAND {0x52}; ///< Dark green palette colour for rough land.
static const uint8_t PC_GRASS_LAND = 0x54; ///< Dark green palette colour for grass land. static constexpr PixelColour PC_GRASS_LAND {0x54}; ///< Dark green palette colour for grass land.
static const uint8_t PC_BARE_LAND = 0x37; ///< Brown palette colour for bare land. static constexpr PixelColour PC_BARE_LAND {0x37}; ///< Brown palette colour for bare land.
static const uint8_t PC_RAINFOREST = 0x5C; ///< Pale green palette colour for rainforest. static constexpr PixelColour PC_RAINFOREST {0x5C}; ///< Pale green palette colour for rainforest.
static const uint8_t PC_FIELDS = 0x25; ///< Light brown palette colour for fields. static constexpr PixelColour PC_FIELDS {0x25}; ///< Light brown palette colour for fields.
static const uint8_t PC_TREES = 0x57; ///< Green palette colour for trees. static constexpr PixelColour PC_TREES {0x57}; ///< Green palette colour for trees.
static const uint8_t PC_WATER = 0xC9; ///< Dark blue palette colour for water. static constexpr PixelColour PC_WATER {0xC9}; ///< Dark blue palette colour for water.
#endif /* PALETTE_FUNC_H */ #endif /* PALETTE_FUNC_H */

View File

@ -234,7 +234,7 @@ public:
/** /**
* Colour on mini-map * Colour on mini-map
*/ */
uint8_t map_colour; PixelColour map_colour;
/** /**
* Introduction date. * Introduction date.

View File

@ -145,7 +145,7 @@ public:
/** /**
* Colour on mini-map * Colour on mini-map
*/ */
uint8_t map_colour; PixelColour map_colour;
/** /**
* Introduction date. * Introduction date.

View File

@ -99,7 +99,7 @@ uint BaseSettingEntry::Draw(GameSettings *settings_ptr, int left, int right, int
int x = rtl ? right : left; int x = rtl ? right : left;
if (cur_row >= first_row) { if (cur_row >= first_row) {
int colour = GetColourGradient(COLOUR_ORANGE, SHADE_NORMAL); PixelColour colour = GetColourGradient(COLOUR_ORANGE, SHADE_NORMAL);
y += (cur_row - first_row) * SETTING_HEIGHT; // Compute correct y start position y += (cur_row - first_row) * SETTING_HEIGHT; // Compute correct y start position
/* Draw vertical for parent nesting levels */ /* Draw vertical for parent nesting levels */

View File

@ -1849,7 +1849,7 @@ void ShowGameOptions()
*/ */
void DrawArrowButtons(int x, int y, Colours button_colour, uint8_t state, bool clickable_left, bool clickable_right) void DrawArrowButtons(int x, int y, Colours button_colour, uint8_t state, bool clickable_left, bool clickable_right)
{ {
int colour = GetColourGradient(button_colour, SHADE_DARKER); PixelColour colour = GetColourGradient(button_colour, SHADE_DARKER);
Dimension dim = NWidgetScrollbar::GetHorizontalDimension(); Dimension dim = NWidgetScrollbar::GetHorizontalDimension();
Rect lr = {x, y, x + (int)dim.width - 1, y + (int)dim.height - 1}; Rect lr = {x, y, x + (int)dim.width - 1, y + (int)dim.height - 1};
@ -1881,7 +1881,7 @@ void DrawArrowButtons(int x, int y, Colours button_colour, uint8_t state, bool c
*/ */
void DrawUpDownButtons(int x, int y, Colours button_colour, uint8_t state, bool clickable_up, bool clickable_down) void DrawUpDownButtons(int x, int y, Colours button_colour, uint8_t state, bool clickable_up, bool clickable_down)
{ {
int colour = GetColourGradient(button_colour, SHADE_DARKER); PixelColour colour = GetColourGradient(button_colour, SHADE_DARKER);
Rect r = {x, y, x + SETTING_BUTTON_WIDTH - 1, y + SETTING_BUTTON_HEIGHT - 1}; Rect r = {x, y, x + SETTING_BUTTON_WIDTH - 1, y + SETTING_BUTTON_HEIGHT - 1};
Rect ur = r.WithWidth(SETTING_BUTTON_WIDTH / 2, (_current_text_dir == TD_RTL)); Rect ur = r.WithWidth(SETTING_BUTTON_WIDTH / 2, (_current_text_dir == TD_RTL));
@ -1907,7 +1907,7 @@ void DrawUpDownButtons(int x, int y, Colours button_colour, uint8_t state, bool
*/ */
void DrawDropDownButton(int x, int y, Colours button_colour, bool state, bool clickable) void DrawDropDownButton(int x, int y, Colours button_colour, bool state, bool clickable)
{ {
int colour = GetColourGradient(button_colour, SHADE_DARKER); PixelColour colour = GetColourGradient(button_colour, SHADE_DARKER);
Rect r = {x, y, x + SETTING_BUTTON_WIDTH - 1, y + SETTING_BUTTON_HEIGHT - 1}; Rect r = {x, y, x + SETTING_BUTTON_WIDTH - 1, y + SETTING_BUTTON_HEIGHT - 1};

View File

@ -45,9 +45,9 @@ void DrawSliderWidget(Rect r, Colours wedge_colour, Colours handle_colour, TextC
int wx1 = r.left + sw / 2; int wx1 = r.left + sw / 2;
int wx2 = r.right - sw / 2; int wx2 = r.right - sw / 2;
if (_current_text_dir == TD_RTL) std::swap(wx1, wx2); if (_current_text_dir == TD_RTL) std::swap(wx1, wx2);
const uint shadow = GetColourGradient(wedge_colour, SHADE_DARK); const PixelColour shadow = GetColourGradient(wedge_colour, SHADE_DARK);
const uint fill = GetColourGradient(wedge_colour, SHADE_LIGHTER); const PixelColour fill = GetColourGradient(wedge_colour, SHADE_LIGHTER);
const uint light = GetColourGradient(wedge_colour, SHADE_LIGHTEST); const PixelColour light = GetColourGradient(wedge_colour, SHADE_LIGHTEST);
const std::array<Point, 3> wedge{ Point{wx1, r.bottom - ha}, Point{wx2, r.top + ha}, Point{wx2, r.bottom - ha} }; const std::array<Point, 3> wedge{ Point{wx1, r.bottom - ha}, Point{wx2, r.top + ha}, Point{wx2, r.bottom - ha} };
GfxFillPolygon(wedge, fill); GfxFillPolygon(wedge, fill);
GfxDrawLine(wedge[0].x, wedge[0].y, wedge[2].x, wedge[2].y, light, t); GfxDrawLine(wedge[0].x, wedge[0].y, wedge[2].x, wedge[2].y, light, t);

View File

@ -44,7 +44,7 @@ static int _smallmap_cargo_count; ///< Number of cargos in the link stats leg
/** Structure for holding relevant data for legends in small map */ /** Structure for holding relevant data for legends in small map */
struct LegendAndColour { struct LegendAndColour {
uint8_t colour; ///< Colour of the item on the map. PixelColour colour; ///< Colour of the item on the map.
StringID legend; ///< String corresponding to the coloured item. StringID legend; ///< String corresponding to the coloured item.
IndustryType type; ///< Type of industry. Only valid for industry entries. IndustryType type; ///< Type of industry. Only valid for industry entries.
uint8_t height; ///< Height in tiles. Only valid for height legend entries. uint8_t height; ///< Height in tiles. Only valid for height legend entries.
@ -63,16 +63,16 @@ static const int NUM_NO_COMPANY_ENTRIES = 4; ///< Number of entries in the owner
#define MK(a, b) {a, b, IT_INVALID, 0, CompanyID::Invalid(), true, false, false} #define MK(a, b) {a, b, IT_INVALID, 0, CompanyID::Invalid(), true, false, false}
/** Macro for a height legend entry with configurable colour. */ /** Macro for a height legend entry with configurable colour. */
#define MC(col_break) {0, STR_TINY_BLACK_HEIGHT, IT_INVALID, 0, CompanyID::Invalid(), true, false, col_break} #define MC(col_break) {{}, STR_TINY_BLACK_HEIGHT, IT_INVALID, 0, CompanyID::Invalid(), true, false, col_break}
/** Macro for non-company owned property entry of LegendAndColour */ /** Macro for non-company owned property entry of LegendAndColour */
#define MO(a, b) {a, b, IT_INVALID, 0, CompanyID::Invalid(), true, false, false} #define MO(a, b) {a, b, IT_INVALID, 0, CompanyID::Invalid(), true, false, false}
/** Macro used for forcing a rebuild of the owner legend the first time it is used. */ /** Macro used for forcing a rebuild of the owner legend the first time it is used. */
#define MOEND() {0, STR_NULL, IT_INVALID, 0, OWNER_NONE, true, true, false} #define MOEND() {{}, STR_NULL, IT_INVALID, 0, OWNER_NONE, true, true, false}
/** Macro for end of list marker in arrays of LegendAndColour */ /** Macro for end of list marker in arrays of LegendAndColour */
#define MKEND() {0, STR_NULL, IT_INVALID, 0, CompanyID::Invalid(), true, true, false} #define MKEND() {{}, STR_NULL, IT_INVALID, 0, CompanyID::Invalid(), true, true, false}
/** /**
* Macro for break marker in arrays of LegendAndColour. * Macro for break marker in arrays of LegendAndColour.
@ -149,7 +149,7 @@ static const LegendAndColour _legend_vegetation[] = {
static LegendAndColour _legend_land_owners[NUM_NO_COMPANY_ENTRIES + MAX_COMPANIES + 1] = { static LegendAndColour _legend_land_owners[NUM_NO_COMPANY_ENTRIES + MAX_COMPANIES + 1] = {
MO(PC_WATER, STR_SMALLMAP_LEGENDA_WATER), MO(PC_WATER, STR_SMALLMAP_LEGENDA_WATER),
MO(0x00, STR_SMALLMAP_LEGENDA_NO_OWNER), // This colour will vary depending on settings. MO({}, STR_SMALLMAP_LEGENDA_NO_OWNER), // This colour will vary depending on settings.
MO(PC_DARK_RED, STR_SMALLMAP_LEGENDA_TOWNS), MO(PC_DARK_RED, STR_SMALLMAP_LEGENDA_TOWNS),
MO(PC_DARK_GREY, STR_SMALLMAP_LEGENDA_INDUSTRIES), MO(PC_DARK_GREY, STR_SMALLMAP_LEGENDA_INDUSTRIES),
/* The legend will be terminated the first time it is used. */ /* The legend will be terminated the first time it is used. */
@ -258,15 +258,15 @@ static const LegendAndColour * const _legend_table[] = {
#define MKCOLOUR(x) TO_LE32(x) #define MKCOLOUR(x) TO_LE32(x)
#define MKCOLOUR_XXXX(x) (MKCOLOUR(0x01010101) * (uint)(x)) #define MKCOLOUR_XXXX(x) (MKCOLOUR(0x01010101) * (uint)(x.p))
#define MKCOLOUR_0XX0(x) (MKCOLOUR(0x00010100) * (uint)(x)) #define MKCOLOUR_0XX0(x) (MKCOLOUR(0x00010100) * (uint)(x.p))
#define MKCOLOUR_X00X(x) (MKCOLOUR(0x01000001) * (uint)(x)) #define MKCOLOUR_X00X(x) (MKCOLOUR(0x01000001) * (uint)(x.p))
#define MKCOLOUR_XYYX(x, y) (MKCOLOUR_X00X(x) | MKCOLOUR_0XX0(y)) #define MKCOLOUR_XYYX(x, y) (MKCOLOUR_X00X(x) | MKCOLOUR_0XX0(y))
#define MKCOLOUR_0000 MKCOLOUR_XXXX(0x00) #define MKCOLOUR_0000 MKCOLOUR_XXXX(PixelColour{0x00})
#define MKCOLOUR_F00F MKCOLOUR_X00X(0xFF) #define MKCOLOUR_F00F MKCOLOUR_X00X(PixelColour{0xFF})
#define MKCOLOUR_FFFF MKCOLOUR_XXXX(0xFF) #define MKCOLOUR_FFFF MKCOLOUR_XXXX(PixelColour{0xFF})
#include "table/heightmap_colours.h" #include "table/heightmap_colours.h"
@ -279,9 +279,9 @@ struct SmallMapColourScheme {
/** Available colour schemes for height maps. */ /** Available colour schemes for height maps. */
static SmallMapColourScheme _heightmap_schemes[] = { static SmallMapColourScheme _heightmap_schemes[] = {
{{}, _green_map_heights, MKCOLOUR_XXXX(0x54)}, ///< Green colour scheme. {{}, _green_map_heights, MKCOLOUR_XXXX(PixelColour{0x54})}, ///< Green colour scheme.
{{}, _dark_green_map_heights, MKCOLOUR_XXXX(0x62)}, ///< Dark green colour scheme. {{}, _dark_green_map_heights, MKCOLOUR_XXXX(PixelColour{0x62})}, ///< Dark green colour scheme.
{{}, _violet_map_heights, MKCOLOUR_XXXX(0x81)}, ///< Violet colour scheme. {{}, _violet_map_heights, MKCOLOUR_XXXX(PixelColour{0x81})}, ///< Violet colour scheme.
}; };
/** /**
@ -329,7 +329,7 @@ void BuildLandLegend()
_legend_land_contours[i].col_break = j % rows == 0; _legend_land_contours[i].col_break = j % rows == 0;
_legend_land_contours[i].end = false; _legend_land_contours[i].end = false;
_legend_land_contours[i].height = j * delta; _legend_land_contours[i].height = j * delta;
_legend_land_contours[i].colour = static_cast<uint8_t>(_heightmap_schemes[_settings_client.gui.smallmap_land_colour].height_colours[_legend_land_contours[i].height]); _legend_land_contours[i].colour = PixelColour{static_cast<uint8_t>(_heightmap_schemes[_settings_client.gui.smallmap_land_colour].height_colours[_legend_land_contours[i].height])};
j++; j++;
} }
_legend_land_contours[i].end = true; _legend_land_contours[i].end = true;
@ -340,7 +340,7 @@ void BuildLandLegend()
*/ */
void BuildOwnerLegend() void BuildOwnerLegend()
{ {
_legend_land_owners[1].colour = static_cast<uint8_t>(_heightmap_schemes[_settings_client.gui.smallmap_land_colour].default_colour); _legend_land_owners[1].colour = PixelColour{static_cast<uint8_t>(_heightmap_schemes[_settings_client.gui.smallmap_land_colour].default_colour)};
int i = NUM_NO_COMPANY_ENTRIES; int i = NUM_NO_COMPANY_ENTRIES;
for (const Company *c : Company::Iterate()) { for (const Company *c : Company::Iterate()) {
@ -607,7 +607,7 @@ uint32_t GetSmallMapOwnerPixels(TileIndex tile, TileType t, IncludeHeightmap inc
} }
/** Vehicle colours in #SMT_VEHICLES mode. Indexed by #VehicleType. */ /** Vehicle colours in #SMT_VEHICLES mode. Indexed by #VehicleType. */
static const uint8_t _vehicle_type_colours[6] = { static const PixelColour _vehicle_type_colours[6] = {
PC_RED, PC_YELLOW, PC_LIGHT_BLUE, PC_WHITE, PC_BLACK, PC_RED PC_RED, PC_YELLOW, PC_LIGHT_BLUE, PC_WHITE, PC_BLACK, PC_RED
}; };
@ -935,7 +935,7 @@ protected:
uint8_t *val8 = (uint8_t *)&val; uint8_t *val8 = (uint8_t *)&val;
int idx = std::max(0, -start_pos); int idx = std::max(0, -start_pos);
for (int pos = std::max(0, start_pos); pos < end_pos; pos++) { for (int pos = std::max(0, start_pos); pos < end_pos; pos++) {
blitter->SetPixel(dst, idx, 0, val8[idx]); blitter->SetPixel(dst, idx, 0, PixelColour{val8[idx]});
idx++; idx++;
} }
/* Switch to next tile in the column */ /* Switch to next tile in the column */
@ -973,7 +973,7 @@ protected:
} }
/* Calculate pointer to pixel and the colour */ /* Calculate pointer to pixel and the colour */
uint8_t colour = (this->map_type == SMT_VEHICLES) ? _vehicle_type_colours[v->type] : PC_WHITE; PixelColour colour = (this->map_type == SMT_VEHICLES) ? _vehicle_type_colours[v->type] : PC_WHITE;
/* And draw either one or two pixels depending on clipping */ /* And draw either one or two pixels depending on clipping */
blitter->SetPixel(dpi->dst_ptr, x, y, colour); blitter->SetPixel(dpi->dst_ptr, x, y, colour);
@ -1348,7 +1348,7 @@ protected:
if (type == _smallmap_industry_highlight) { if (type == _smallmap_industry_highlight) {
if (_smallmap_industry_highlight_state) return MKCOLOUR_XXXX(PC_WHITE); if (_smallmap_industry_highlight_state) return MKCOLOUR_XXXX(PC_WHITE);
} else { } else {
return GetIndustrySpec(type)->map_colour * 0x01010101; return GetIndustrySpec(type)->map_colour.p * 0x01010101;
} }
} }
/* Otherwise make it disappear */ /* Otherwise make it disappear */
@ -1644,7 +1644,7 @@ public:
i = 1; i = 1;
} }
uint8_t legend_colour = tbl->colour; PixelColour legend_colour = tbl->colour;
std::array<StringParameter, 2> params{}; std::array<StringParameter, 2> params{};
switch (this->map_type) { switch (this->map_type) {

View File

@ -5079,7 +5079,7 @@ StationIDStack FlowStatMap::DeleteFlows(StationID via)
FlowStat &s_flows = f_it->second; FlowStat &s_flows = f_it->second;
s_flows.ChangeShare(via, INT_MIN); s_flows.ChangeShare(via, INT_MIN);
if (s_flows.GetShares()->empty()) { if (s_flows.GetShares()->empty()) {
ret.Push(f_it->first.base()); ret.Push(f_it->first);
this->erase(f_it++); this->erase(f_it++);
} else { } else {
++f_it; ++f_it;

View File

@ -225,7 +225,7 @@ static void StationsWndShowStationRating(int left, int right, int y, CargoType c
int padding = ScaleGUITrad(1); int padding = ScaleGUITrad(1);
int width = right - left; int width = right - left;
int colour = cs->rating_colour; PixelColour colour = cs->rating_colour;
TextColour tc = GetContrastColour(colour); TextColour tc = GetContrastColour(colour);
uint w = std::min(amount + 5, units_full) * width / units_full; uint w = std::min(amount + 5, units_full) * width / units_full;

View File

@ -26,7 +26,7 @@ struct RoadStop;
struct StationSpec; struct StationSpec;
struct Waypoint; struct Waypoint;
using StationIDStack = SmallStack<StationID::BaseType, StationID::BaseType, StationID::Invalid().base(), 8, StationID::End().base()>; using StationIDStack = SmallStack<StationID, StationID::BaseType, StationID::Invalid().base(), 8, StationID::End().base()>;
/** Station types */ /** Station types */
enum class StationType : uint8_t { enum class StationType : uint8_t {

View File

@ -1134,7 +1134,7 @@ enum IndustryTypes : uint8_t {
{r1, r2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, m, \ {r1, r2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, m, \
{INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO}, \ {INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO}, \
{{im1, 0}, {im2, 0}, {im3, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}}, \ {{im1, 0}, {im2, 0}, {im3, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}}, \
pr, clim, bev, col, in, intx, s1, s2, s3, STR_UNDEFINED, {ai1, ai2, ai3, ai4}, {ag1, ag2, ag3, ag4}, \ pr, clim, bev, PixelColour{col}, in, intx, s1, s2, s3, STR_UNDEFINED, {ai1, ai2, ai3, ai4}, {ag1, ag2, ag3, ag4}, \
IndustryCallbackMasks{}, true, SubstituteGRFFileProps(IT_INVALID), snd, {}, \ IndustryCallbackMasks{}, true, SubstituteGRFFileProps(IT_INVALID), snd, {}, \
{{p1, p2}}, {{a1, a2, a3}}} {{p1, p2}}, {{a1, a2, a3}}}
/* Format: /* Format:

View File

@ -49,7 +49,7 @@
* @param classes Classes of this cargo type. @see CargoClass * @param classes Classes of this cargo type. @see CargoClass
*/ */
#define MK(bt, label, colour, weight, mult, ip, td1, td2, freight, tae, str_plural, str_singular, str_volume, classes) \ #define MK(bt, label, colour, weight, mult, ip, td1, td2, freight, tae, str_plural, str_singular, str_volume, classes) \
{label, bt, colour, colour, weight, mult, classes, ip, {td1, td2}, freight, tae, INVALID_TPE, TOWN_PRODUCTION_DIVISOR, CargoCallbackMasks{}, \ {label, bt, PixelColour{colour}, PixelColour{colour}, weight, mult, classes, ip, {td1, td2}, freight, tae, INVALID_TPE, TOWN_PRODUCTION_DIVISOR, CargoCallbackMasks{}, \
MK_STR_CARGO_PLURAL(str_plural), MK_STR_CARGO_SINGULAR(str_singular), str_volume, MK_STR_QUANTITY(str_plural), MK_STR_ABBREV(str_plural), \ MK_STR_CARGO_PLURAL(str_plural), MK_STR_CARGO_SINGULAR(str_singular), str_volume, MK_STR_QUANTITY(str_plural), MK_STR_ABBREV(str_plural), \
MK_SPRITE(str_plural), nullptr, nullptr, 0} MK_SPRITE(str_plural), nullptr, nullptr, 0}

View File

@ -98,7 +98,7 @@ static const RailTypeInfo _original_railtypes[] = {
RailTypeLabelList(), RailTypeLabelList(),
/* map colour */ /* map colour */
0x0A, PC_GREY,
/* introduction date */ /* introduction date */
CalendarTime::INVALID_DATE, CalendarTime::INVALID_DATE,
@ -200,7 +200,7 @@ static const RailTypeInfo _original_railtypes[] = {
RailTypeLabelList(), RailTypeLabelList(),
/* map colour */ /* map colour */
0x0A, PC_GREY,
/* introduction date */ /* introduction date */
CalendarTime::INVALID_DATE, CalendarTime::INVALID_DATE,
@ -298,7 +298,7 @@ static const RailTypeInfo _original_railtypes[] = {
RailTypeLabelList(), RailTypeLabelList(),
/* map colour */ /* map colour */
0x0A, PC_GREY,
/* introduction date */ /* introduction date */
CalendarTime::INVALID_DATE, CalendarTime::INVALID_DATE,
@ -396,7 +396,7 @@ static const RailTypeInfo _original_railtypes[] = {
RailTypeLabelList(), RailTypeLabelList(),
/* map colour */ /* map colour */
0x0A, PC_GREY,
/* introduction date */ /* introduction date */
CalendarTime::INVALID_DATE, CalendarTime::INVALID_DATE,

View File

@ -81,7 +81,7 @@ static const RoadTypeInfo _original_roadtypes[] = {
RoadTypeLabelList(), RoadTypeLabelList(),
/* map colour */ /* map colour */
0x01, PC_BLACK,
/* introduction date */ /* introduction date */
CalendarTime::MIN_DATE, CalendarTime::MIN_DATE,
@ -162,7 +162,7 @@ static const RoadTypeInfo _original_roadtypes[] = {
RoadTypeLabelList(), RoadTypeLabelList(),
/* map colour */ /* map colour */
0x01, PC_BLACK,
/* introduction date */ /* introduction date */
CalendarTime::INVALID_DATE, CalendarTime::INVALID_DATE,

View File

@ -8,22 +8,22 @@
/** @file string_colours.h The colour translation of GRF's strings. */ /** @file string_colours.h The colour translation of GRF's strings. */
/** Colour mapping for #TextColour. */ /** Colour mapping for #TextColour. */
static const uint8_t _string_colourmap[17] = { static constexpr PixelColour _string_colourmap[17] = {
150, // TC_BLUE PixelColour{150}, // TC_BLUE
12, // TC_SILVER PixelColour{ 12}, // TC_SILVER
189, // TC_GOLD PixelColour{189}, // TC_GOLD
184, // TC_RED PixelColour{184}, // TC_RED
174, // TC_PURPLE PixelColour{174}, // TC_PURPLE
30, // TC_LIGHT_BROWN PixelColour{ 30}, // TC_LIGHT_BROWN
195, // TC_ORANGE PixelColour{195}, // TC_ORANGE
209, // TC_GREEN PixelColour{209}, // TC_GREEN
68, // TC_YELLOW PixelColour{ 68}, // TC_YELLOW
95, // TC_DARK_GREEN PixelColour{ 95}, // TC_DARK_GREEN
79, // TC_CREAM PixelColour{ 79}, // TC_CREAM
116, // TC_BROWN PixelColour{116}, // TC_BROWN
15, // TC_WHITE PixelColour{ 15}, // TC_WHITE
152, // TC_LIGHT_BLUE PixelColour{152}, // TC_LIGHT_BLUE
6, // TC_GREY PixelColour{ 6}, // TC_GREY
133, // TC_DARK_BLUE PixelColour{133}, // TC_DARK_BLUE
1, // TC_BLACK PixelColour{ 1}, // TC_BLACK
}; };

View File

@ -21,8 +21,6 @@ public:
this->height = FontCache::GetDefaultFontHeight(this->fs); this->height = FontCache::GetDefaultFontHeight(this->fs);
} }
void SetUnicodeGlyph(char32_t, SpriteID) override {}
void InitializeUnicodeGlyphMap() override {}
void ClearFontCache() override {} void ClearFontCache() override {}
const Sprite *GetGlyph(GlyphID) override { return nullptr; } const Sprite *GetGlyph(GlyphID) override { return nullptr; }
uint GetGlyphWidth(GlyphID) override { return this->height / 2; } uint GetGlyphWidth(GlyphID) override { return this->height / 2; }

View File

@ -741,7 +741,7 @@ public:
*/ */
inline StationIDStack GetNextStoppingStation() const inline StationIDStack GetNextStoppingStation() const
{ {
return (this->orders == nullptr) ? StationID::Invalid().base() : this->orders->GetNextStoppingStation(this); return (this->orders == nullptr) ? StationID::Invalid() : this->orders->GetNextStoppingStation(this);
} }
void ResetRefitCaps(); void ResetRefitCaps();

View File

@ -697,7 +697,7 @@ static void DrawVehicleRefitWindow(const RefitOptions &refits, const RefitOption
bool rtl = _current_text_dir == TD_RTL; bool rtl = _current_text_dir == TD_RTL;
uint iconwidth = std::max(GetSpriteSize(SPR_CIRCLE_FOLDED).width, GetSpriteSize(SPR_CIRCLE_UNFOLDED).width); uint iconwidth = std::max(GetSpriteSize(SPR_CIRCLE_FOLDED).width, GetSpriteSize(SPR_CIRCLE_UNFOLDED).width);
uint iconheight = GetSpriteSize(SPR_CIRCLE_FOLDED).height; uint iconheight = GetSpriteSize(SPR_CIRCLE_FOLDED).height;
int linecolour = GetColourGradient(COLOUR_ORANGE, SHADE_NORMAL); PixelColour linecolour = GetColourGradient(COLOUR_ORANGE, SHADE_NORMAL);
int iconleft = rtl ? ir.right - iconwidth : ir.left; int iconleft = rtl ? ir.right - iconwidth : ir.left;
int iconcenter = rtl ? ir.right - iconwidth / 2 : ir.left + iconwidth / 2; int iconcenter = rtl ? ir.right - iconwidth / 2 : ir.left + iconwidth / 2;

View File

@ -1732,13 +1732,13 @@ static void ViewportDrawDirtyBlocks()
int right = UnScaleByZoom(dpi->width, dpi->zoom); int right = UnScaleByZoom(dpi->width, dpi->zoom);
int bottom = UnScaleByZoom(dpi->height, dpi->zoom); int bottom = UnScaleByZoom(dpi->height, dpi->zoom);
int colour = _string_colourmap[_dirty_block_colour & 0xF]; PixelColour colour = _string_colourmap[_dirty_block_colour & 0xF];
dst = dpi->dst_ptr; dst = dpi->dst_ptr;
uint8_t bo = UnScaleByZoom(dpi->left + dpi->top, dpi->zoom) & 1; uint8_t bo = UnScaleByZoom(dpi->left + dpi->top, dpi->zoom) & 1;
do { do {
for (int i = (bo ^= 1); i < right; i += 2) blitter->SetPixel(dst, i, 0, (uint8_t)colour); for (int i = (bo ^= 1); i < right; i += 2) blitter->SetPixel(dst, i, 0, colour);
dst = blitter->MoveTo(dst, 0, 1); dst = blitter->MoveTo(dst, 0, 1);
} while (--bottom > 0); } while (--bottom > 0);
} }
@ -1761,7 +1761,7 @@ static void ViewportDrawStrings(ZoomLevel zoom, const StringSpriteToDrawVector *
} }
if (ss.flags.Test(ViewportStringFlag::TextColour)) { if (ss.flags.Test(ViewportStringFlag::TextColour)) {
if (ss.colour != INVALID_COLOUR) colour = static_cast<TextColour>(GetColourGradient(ss.colour, SHADE_LIGHTER) | TC_IS_PALETTE_COLOUR); if (ss.colour != INVALID_COLOUR) colour = GetColourGradient(ss.colour, SHADE_LIGHTER).ToTextColour();
} }
int left = x + WidgetDimensions::scaled.fullbevel.left; int left = x + WidgetDimensions::scaled.fullbevel.left;

View File

@ -302,11 +302,11 @@ void DrawFrameRect(int left, int top, int right, int bottom, Colours colour, Fra
} else { } else {
assert(colour < COLOUR_END); assert(colour < COLOUR_END);
const uint dark = GetColourGradient(colour, SHADE_DARK); const PixelColour dark = GetColourGradient(colour, SHADE_DARK);
const uint medium_dark = GetColourGradient(colour, SHADE_LIGHT); const PixelColour medium_dark = GetColourGradient(colour, SHADE_LIGHT);
const uint medium_light = GetColourGradient(colour, SHADE_LIGHTER); const PixelColour medium_light = GetColourGradient(colour, SHADE_LIGHTER);
const uint light = GetColourGradient(colour, SHADE_LIGHTEST); const PixelColour light = GetColourGradient(colour, SHADE_LIGHTEST);
uint interior; PixelColour interior;
Rect outer = {left, top, right, bottom}; // Outside rectangle Rect outer = {left, top, right, bottom}; // Outside rectangle
Rect inner = outer.Shrink(WidgetDimensions::scaled.bevel); // Inside rectangle Rect inner = outer.Shrink(WidgetDimensions::scaled.bevel); // Inside rectangle
@ -469,7 +469,7 @@ static inline void DrawMatrix(const Rect &r, Colours colour, bool clicked, uint3
row_height = r.Height() / num_rows; row_height = r.Height() / num_rows;
} }
int col = GetColourGradient(colour, SHADE_LIGHTER); PixelColour col = GetColourGradient(colour, SHADE_LIGHTER);
int x = r.left; int x = r.left;
for (int ctr = num_columns; ctr > 1; ctr--) { for (int ctr = num_columns; ctr > 1; ctr--) {
@ -515,8 +515,8 @@ static inline void DrawVerticalScrollbar(const Rect &r, Colours colour, bool up_
DrawImageButtons(r.WithHeight(height, false), NWID_VSCROLLBAR, colour, up_clicked, SPR_ARROW_UP, SA_CENTER); DrawImageButtons(r.WithHeight(height, false), NWID_VSCROLLBAR, colour, up_clicked, SPR_ARROW_UP, SA_CENTER);
DrawImageButtons(r.WithHeight(height, true), NWID_VSCROLLBAR, colour, down_clicked, SPR_ARROW_DOWN, SA_CENTER); DrawImageButtons(r.WithHeight(height, true), NWID_VSCROLLBAR, colour, down_clicked, SPR_ARROW_DOWN, SA_CENTER);
int c1 = GetColourGradient(colour, SHADE_DARK); PixelColour c1 = GetColourGradient(colour, SHADE_DARK);
int c2 = GetColourGradient(colour, SHADE_LIGHTEST); PixelColour c2 = GetColourGradient(colour, SHADE_LIGHTEST);
/* draw "shaded" background */ /* draw "shaded" background */
GfxFillRect(r.left, r.top + height, r.right, r.bottom - height, c2); GfxFillRect(r.left, r.top + height, r.right, r.bottom - height, c2);
@ -554,8 +554,8 @@ static inline void DrawHorizontalScrollbar(const Rect &r, Colours colour, bool l
DrawImageButtons(r.WithWidth(width, false), NWID_HSCROLLBAR, colour, left_clicked, SPR_ARROW_LEFT, SA_CENTER); DrawImageButtons(r.WithWidth(width, false), NWID_HSCROLLBAR, colour, left_clicked, SPR_ARROW_LEFT, SA_CENTER);
DrawImageButtons(r.WithWidth(width, true), NWID_HSCROLLBAR, colour, right_clicked, SPR_ARROW_RIGHT, SA_CENTER); DrawImageButtons(r.WithWidth(width, true), NWID_HSCROLLBAR, colour, right_clicked, SPR_ARROW_RIGHT, SA_CENTER);
int c1 = GetColourGradient(colour, SHADE_DARK); PixelColour c1 = GetColourGradient(colour, SHADE_DARK);
int c2 = GetColourGradient(colour, SHADE_LIGHTEST); PixelColour c2 = GetColourGradient(colour, SHADE_LIGHTEST);
/* draw "shaded" background */ /* draw "shaded" background */
GfxFillRect(r.left + width, r.top, r.right - width, r.bottom, c2); GfxFillRect(r.left + width, r.top, r.right - width, r.bottom, c2);
@ -593,8 +593,8 @@ static inline void DrawFrame(const Rect &r, Colours colour, TextColour text_colo
if (!str.empty()) x2 = DrawString(r.left + WidgetDimensions::scaled.frametext.left, r.right - WidgetDimensions::scaled.frametext.right, r.top, str, text_colour, align, false, fs); if (!str.empty()) x2 = DrawString(r.left + WidgetDimensions::scaled.frametext.left, r.right - WidgetDimensions::scaled.frametext.right, r.top, str, text_colour, align, false, fs);
int c1 = GetColourGradient(colour, SHADE_DARK); PixelColour c1 = GetColourGradient(colour, SHADE_DARK);
int c2 = GetColourGradient(colour, SHADE_LIGHTEST); PixelColour c2 = GetColourGradient(colour, SHADE_LIGHTEST);
/* If the frame has text, adjust the top bar to fit half-way through */ /* If the frame has text, adjust the top bar to fit half-way through */
Rect inner = r.Shrink(ScaleGUITrad(1)); Rect inner = r.Shrink(ScaleGUITrad(1));
@ -791,7 +791,7 @@ void Window::DrawWidgets() const
Rect outer = widget->GetCurrentRect(); Rect outer = widget->GetCurrentRect();
Rect inner = outer.Shrink(WidgetDimensions::scaled.bevel).Expand(1); Rect inner = outer.Shrink(WidgetDimensions::scaled.bevel).Expand(1);
int colour = _string_colourmap[_window_highlight_colour ? widget->GetHighlightColour() : TC_WHITE]; PixelColour colour = _string_colourmap[_window_highlight_colour ? widget->GetHighlightColour() : TC_WHITE];
GfxFillRect(outer.left, outer.top, inner.left, inner.bottom, colour); GfxFillRect(outer.left, outer.top, inner.left, inner.bottom, colour);
GfxFillRect(inner.left + 1, outer.top, inner.right - 1, inner.top, colour); GfxFillRect(inner.left + 1, outer.top, inner.right - 1, inner.top, colour);