A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 270 lines 7.3 kB view raw
1/*************************************************************************** 2 * __________ __ ___. 3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 7 * \/ \/ \/ \/ \/ 8 * $Id$ 9 * 10 * Copyright (C) 2010 Robert Bieber 11 * 12 * This program is free software; you can redistribute it and/or 13 * modify it under the terms of the GNU General Public License 14 * as published by the Free Software Foundation; either version 2 15 * of the License, or (at your option) any later version. 16 * 17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 18 * KIND, either express or implied. 19 * 20 ****************************************************************************/ 21 22#include "rbfont.h" 23#include "rbfontcache.h" 24#include "rbtextcache.h" 25 26#include <QFont> 27#include <QBrush> 28#include <QFile> 29#include <QPainter> 30#include <QBitmap> 31#include <QImage> 32#include <QSettings> 33 34#include <QDebug> 35 36quint16 RBFont::maxFontSizeFor16BitOffsets = 0xFFDB; 37 38RBFont::RBFont(QString file) 39 : valid(false), imageData(0), offsetData(0), widthData(0) 40{ 41 42 bool badFile = false; 43 44 /* Attempting to locate the correct file name */ 45 if(!QFile::exists(file)) 46 { 47 /* Checking in the fonts repository */ 48 QSettings settings; 49 settings.beginGroup("RBFont"); 50 51 file = file.split("/").last(); 52 file = settings.value("fontDir", "").toString() + "/" + file; 53 54 settings.endGroup(); 55 56 if(!QFile::exists(file)) 57 { 58 file = ":/fonts/08-Schumacher-Clean.fnt"; 59 60 badFile = true; 61 } 62 } 63 header.insert("filename", file); 64 65 /* Checking for a cache entry */ 66 RBFontCache::CacheInfo* cache = RBFontCache::lookup(file); 67 if(cache) 68 { 69 imageData = cache->imageData; 70 offsetData = cache->offsetData; 71 widthData = cache->widthData; 72 header = cache->header; 73 74 if(!badFile) 75 valid = true; 76 77 return; 78 } 79 80 /* Opening the file */ 81 QFile fin(file); 82 fin.open(QFile::ReadOnly); 83 84 /* Loading the header info */ 85 quint8 byte; 86 quint16 word; 87 quint32 dword; 88 89 QDataStream data(&fin); 90 data.setByteOrder(QDataStream::LittleEndian); 91 92 /* Grabbing the magic number and version */ 93 data >> dword; 94 header.insert("version", dword); 95 96 /* Max font width */ 97 data >> word; 98 header.insert("maxwidth", word); 99 100 /* Font height */ 101 data >> word; 102 header.insert("height", word); 103 104 /* Ascent */ 105 data >> word; 106 header.insert("ascent", word); 107 108 /* Padding */ 109 data >> word; 110 111 /* First character code */ 112 data >> dword; 113 header.insert("firstchar", dword); 114 115 /* Default character code */ 116 data >> dword; 117 header.insert("defaultchar", dword); 118 119 /* Number of characters */ 120 data >> dword; 121 header.insert("size", dword); 122 123 /* Bytes of imagebits in file */ 124 data >> dword; 125 header.insert("nbits", dword); 126 127 /* Longs (dword) of offset data in file */ 128 data >> dword; 129 header.insert("noffset", dword); 130 131 /* Bytes of width data in file */ 132 data >> dword; 133 header.insert("nwidth", dword); 134 135 /* Loading the image data */ 136 imageData = new quint8[header.value("nbits").toInt()]; 137 data.readRawData(reinterpret_cast<char*>(imageData), 138 header.value("nbits").toInt()); 139 140 /* Aligning on 16-bit boundary */ 141 if(header.value("nbits").toInt() % 2 == 1) 142 data >> byte; 143 144 /* Loading the offset table if necessary */ 145 if(header.value("noffset").toInt() > 0) 146 { 147 int bytesToRead; 148 if(header.value("nbits").toInt() > maxFontSizeFor16BitOffsets) 149 bytesToRead = 4 * header.value("noffset").toInt(); 150 else 151 bytesToRead = 2 * header.value("noffset").toInt(); 152 offsetData = new quint16[bytesToRead]; 153 data.readRawData(reinterpret_cast<char*>(offsetData), bytesToRead); 154 } 155 156 /* Loading the width table if necessary */ 157 if(header.value("nwidth").toInt() > 0) 158 { 159 widthData = new quint8[header.value("nwidth").toInt()]; 160 data.readRawData(reinterpret_cast<char*>(widthData), 161 header.value("nwidth").toInt()); 162 } 163 164 fin.close(); 165 166 /* Caching the font data */ 167 cache = new RBFontCache::CacheInfo; 168 cache->imageData = imageData; 169 cache->offsetData = offsetData; 170 cache->widthData = widthData; 171 cache->header = header; 172 RBFontCache::insert(file, cache); 173 174 if(!badFile) 175 valid = true; 176 177} 178 179RBFont::~RBFont() 180{ 181} 182 183RBText* RBFont::renderText(QString text, QColor color, int viewWidth, 184 QGraphicsItem *parent) 185{ 186 187 /* Checking for a cache hit first */ 188 QImage* image = RBTextCache::lookup(header.value("filename").toString() 189 + text); 190 if(image) 191 return new RBText(image, viewWidth, parent); 192 193 int firstChar = header.value("firstchar").toInt(); 194 int height = header.value("height").toInt(); 195 int maxWidth = header.value("maxwidth").toInt(); 196 197 bool extendedSet = header.value("nbits"). 198 toUInt() > maxFontSizeFor16BitOffsets; 199 200 /* First we determine the width of the combined text */ 201 QList<int> widths; 202 for(int i = 0; i < text.length(); i++) 203 { 204 if(widthData) 205 widths.append(widthData[text[i].unicode() - firstChar]); 206 else 207 widths.append(maxWidth); 208 } 209 210 int totalWidth = 0; 211 for(int i = 0; i < widths.count(); i++) 212 totalWidth += widths[i]; 213 214 image = new QImage(totalWidth, height, QImage::Format_Indexed8); 215 216 image->setColor(0, qRgba(0,0,0,0)); 217 image->setColor(1, color.rgb()); 218 219 /* Drawing the text */ 220 int startX = 0; 221 for(int i = 0; i < text.length(); i++) 222 { 223 unsigned int offset; 224 if(offsetData) 225 { 226 if(extendedSet) 227 offset = reinterpret_cast<quint32*>(offsetData)[text[i].unicode() - firstChar]; 228 else 229 offset = offsetData[text[i].unicode() - firstChar]; 230 } 231 else 232 { 233 offset = (text[i].unicode() - firstChar) * maxWidth; 234 } 235 236 int bytesHigh = height / 8; 237 if(height % 8 > 0) 238 bytesHigh++; 239 240 int bytes = bytesHigh * widths[i]; 241 242 for(int byte = 0; byte < bytes; byte++) 243 { 244 int x = startX + byte % widths[i]; 245 int y = byte / widths[i] * 8; 246 quint8 data = imageData[offset]; 247 quint8 mask = 0x1; 248 for(int bit = 0; bit < 8; bit++) 249 { 250 if(mask & data) 251 image->setPixel(x, y, 1); 252 else 253 image->setPixel(x, y, 0); 254 255 y++; 256 mask <<= 1; 257 if(y >= height) 258 break; 259 } 260 261 offset++; 262 } 263 264 startX += widths[i]; 265 } 266 267 RBTextCache::insert(header.value("filename").toString() + text, image); 268 return new RBText(image, viewWidth, parent); 269 270}