You've already forked crosspoint-reader
mirror of
https://github.com/crosspoint-reader/crosspoint-reader.git
synced 2026-04-29 10:26:52 -07:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 567fa6e1e2 |
@@ -15,5 +15,6 @@ fi
|
||||
# Additionally exclude files in 'lib/EpdFont/builtinFonts/' as they are script-generated.
|
||||
git ls-files --exclude-standard ${GIT_LS_FILES_FLAGS} \
|
||||
| grep -E '\.(c|cpp|h|hpp)$' \
|
||||
| grep -v -E '^lib/EpdFont/builtinFonts/' \
|
||||
| grep -v -E '^lib/CrossPointFont/builtinFonts/' \
|
||||
| grep -v -E '^lib/CrossPointFont/Group5' \
|
||||
| xargs -r clang-format -style=file -i
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
#include "CrossPointFont.h"
|
||||
|
||||
#include <Utf8.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
#define FONT_SCALE 2
|
||||
|
||||
namespace {
|
||||
// Number of set bits from 0->15
|
||||
uint8_t bitCount[] = {
|
||||
0, // 0b0000,
|
||||
1, // 0b0001,
|
||||
1, // 0b0010,
|
||||
2, // 0b0011,
|
||||
1, // 0b0100,
|
||||
2, // 0b0101,
|
||||
2, // 0b0110,
|
||||
3, // 0b0111,
|
||||
1, // 0b1000,
|
||||
2, // 0b1001,
|
||||
2, // 0b1010,
|
||||
3, // 0b1011,
|
||||
2, // 0b1100,
|
||||
3, // 0b1101,
|
||||
3, // 0b1110,
|
||||
4, // 0b1111,
|
||||
};
|
||||
} // namespace
|
||||
|
||||
void CrossPointFont::getTextBounds(const char* string, const Style style, const int startX, const int startY, int* minX,
|
||||
int* minY, int* maxX, int* maxY) const {
|
||||
*minX = startX;
|
||||
*minY = startY;
|
||||
*maxX = startX;
|
||||
*maxY = startY;
|
||||
|
||||
if (*string == '\0') {
|
||||
return;
|
||||
}
|
||||
|
||||
int cursorX = startX;
|
||||
const int cursorY = startY;
|
||||
uint32_t cp;
|
||||
while ((cp = utf8NextCodepoint(reinterpret_cast<const uint8_t**>(&string)))) {
|
||||
const CrossPointFontGlyph* glyph = getGlyph(cp, style);
|
||||
|
||||
if (!glyph) {
|
||||
glyph = getGlyph(REPLACEMENT_GLYPH, style);
|
||||
}
|
||||
|
||||
if (!glyph) {
|
||||
// TODO: Better handle this?
|
||||
continue;
|
||||
}
|
||||
|
||||
*minX = std::min(*minX, cursorX + glyph->xOffset / FONT_SCALE);
|
||||
*maxX = std::max(*maxX, cursorX + (glyph->xOffset + glyph->width) / FONT_SCALE);
|
||||
*minY = std::min(*minY, cursorY + (glyph->yOffset - glyph->height) / FONT_SCALE);
|
||||
*maxY = std::max(*maxY, cursorY + glyph->yOffset / FONT_SCALE);
|
||||
cursorX += glyph->xAdvance / FONT_SCALE;
|
||||
}
|
||||
}
|
||||
|
||||
void CrossPointFont::getTextDimensions(const char* string, const Style style, int* w, int* h) const {
|
||||
int minX = 0, minY = 0, maxX = 0, maxY = 0;
|
||||
|
||||
getTextBounds(string, style, 0, 0, &minX, &minY, &maxX, &maxY);
|
||||
|
||||
*w = maxX - minX;
|
||||
*h = maxY - minY;
|
||||
}
|
||||
|
||||
uint8_t CrossPointFont::styleGroup(const Style style) const {
|
||||
if (style == REGULAR) return 0;
|
||||
|
||||
if (data.header.styles == 0b0001) {
|
||||
// Only have regular font, show regular
|
||||
return 0;
|
||||
}
|
||||
if (data.header.styles == 0b0011) {
|
||||
// Only have bold and regular font
|
||||
// Show bold if style is bold or bold_italic
|
||||
return style == BOLD || style == BOLD_ITALIC ? 1 : 0;
|
||||
}
|
||||
if (data.header.styles == 0b0101) {
|
||||
// Only have italic and regular font
|
||||
// Show italic if style is italic or bold_italic
|
||||
return style == ITALIC || style == BOLD_ITALIC ? 1 : 0;
|
||||
}
|
||||
if (data.header.styles == 0b1001) {
|
||||
// Only have bold_italic and regular font
|
||||
// Show bold_italic if style is any non-regular
|
||||
return style == BOLD_ITALIC || style == BOLD || style == ITALIC ? 1 : 0;
|
||||
}
|
||||
if (data.header.styles == 0b0111) {
|
||||
// Have all but bold_italic
|
||||
// Show bold if style is bold_italic, otherwise show the requested style
|
||||
return style == BOLD_ITALIC ? 1 : style;
|
||||
}
|
||||
if (data.header.styles == 0b1011) {
|
||||
// Have all but italic
|
||||
// Show bold_italic if style is italic, otherwise show the requested style
|
||||
return style == ITALIC ? 2 : style;
|
||||
}
|
||||
if (data.header.styles == 0b1101) {
|
||||
// Have all but bold
|
||||
// Show bold_italic if style is bold, otherwise show the requested style
|
||||
return style == BOLD ? 2 : style;
|
||||
}
|
||||
if (data.header.styles == 0b1111) {
|
||||
return style;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const CrossPointFontGlyph* CrossPointFont::getGlyph(const uint32_t cp, const Style style) const {
|
||||
const CrossPointFontUnicodeInterval* intervals = data.intervals;
|
||||
const int count = data.header.intervalCount;
|
||||
|
||||
if (count == 0) return nullptr;
|
||||
|
||||
// Binary search for O(log n) lookup instead of O(n)
|
||||
// Critical for Korean fonts with many unicode intervals
|
||||
int left = 0;
|
||||
int right = count - 1;
|
||||
|
||||
while (left <= right) {
|
||||
const int mid = left + (right - left) / 2;
|
||||
const CrossPointFontUnicodeInterval* interval = &intervals[mid];
|
||||
|
||||
if (cp < interval->first) {
|
||||
right = mid - 1;
|
||||
} else if (cp > interval->last) {
|
||||
left = mid + 1;
|
||||
} else {
|
||||
// Found: cp >= interval->first && cp <= interval->last
|
||||
const uint32_t index =
|
||||
interval->offset + (cp - interval->first) * bitCount[data.header.styles] + styleGroup(style);
|
||||
return &data.glyphs[index];
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "CrossPointFontFormat.h"
|
||||
#include "Group5/Group5.h"
|
||||
|
||||
class CrossPointFont {
|
||||
public:
|
||||
enum Style : uint8_t { REGULAR = 0, BOLD = 1, ITALIC = 2, BOLD_ITALIC = 3 };
|
||||
|
||||
CrossPointFontData data;
|
||||
explicit CrossPointFont(void* rawData) {
|
||||
data.header = *static_cast<CrossPointFontHeader*>(rawData);
|
||||
data.intervals = (CrossPointFontUnicodeInterval*)(static_cast<uint8_t*>(rawData) + sizeof(CrossPointFontHeader));
|
||||
data.glyphs = (CrossPointFontGlyph*)((uint8_t*)data.intervals +
|
||||
sizeof(CrossPointFontUnicodeInterval) * data.header.intervalCount);
|
||||
data.bitmap = (uint8_t*)data.glyphs + sizeof(CrossPointFontGlyph) * data.header.glyphCount;
|
||||
}
|
||||
|
||||
~CrossPointFont() = default;
|
||||
void getTextDimensions(const char* string, Style style, int* w, int* h) const;
|
||||
const CrossPointFontGlyph* getGlyph(uint32_t cp, Style style) const;
|
||||
|
||||
private:
|
||||
void getTextBounds(const char* string, Style style, int startX, int startY, int* minX, int* minY, int* maxX,
|
||||
int* maxY) const;
|
||||
uint8_t styleGroup(Style style) const;
|
||||
};
|
||||
|
||||
// TODO: CrossPointFontSmall
|
||||
@@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
// 16-bit marker at the start of a CrossPoint font file
|
||||
// (CrossPoint Font Format)
|
||||
#define CPF_FONT_MARKER 0xCFF1
|
||||
#define CPF_FONT_MARKER_SMALL 0xCFF2
|
||||
|
||||
// Font info per large character (glyph)
|
||||
typedef struct {
|
||||
uint32_t bitmapOffset; /// Offset to compressed bitmap data for this glyph
|
||||
uint16_t width; /// bitmap width in pixels
|
||||
uint16_t height; /// bitmap height in pixels
|
||||
uint16_t xAdvance; /// total width in pixels (bitmap + padding)
|
||||
int16_t xOffset; /// left padding to upper left corner
|
||||
int16_t yOffset; /// top padding to upper left corner
|
||||
} CrossPointFontGlyph;
|
||||
|
||||
// Font info per small character (glyph)
|
||||
typedef struct {
|
||||
uint32_t bitmapOffset; /// Offset to compressed bitmap data for this glyph
|
||||
uint8_t width; /// bitmap width in pixels
|
||||
uint8_t height; /// bitmap height in pixels
|
||||
uint8_t xAdvance; /// total width in pixels (bitmap + padding)
|
||||
int8_t xOffset; /// left padding to upper left corner
|
||||
int16_t yOffset; /// top padding to upper left corner
|
||||
} CrossPointFontSmallGlyph;
|
||||
|
||||
/// Glyph interval structure
|
||||
typedef struct {
|
||||
uint32_t first; /// The first unicode code point of the interval
|
||||
uint32_t last; /// The last unicode code point of the interval
|
||||
uint32_t offset; /// Index of the first code point into the glyph array
|
||||
} CrossPointFontUnicodeInterval;
|
||||
|
||||
typedef struct {
|
||||
uint16_t u16Marker; /// CPF_FONT_MARKER / CPF_FONT_MARKER_SMALL
|
||||
uint16_t height; /// Newline distance (y axis)
|
||||
uint16_t ascender; /// Maximal height of a glyph above the base line
|
||||
uint8_t styles; /// Regular = 0x01, Bold = 0x02, Italic = 0x04, BoldItalic = 0x08, can be OR'd together
|
||||
uint16_t intervalCount; /// Number of unicode intervals.
|
||||
uint32_t glyphCount; /// Number of total glyphs across all styles
|
||||
} CrossPointFontHeader;
|
||||
|
||||
/// Data stored for FONT AS A WHOLE
|
||||
typedef struct {
|
||||
CrossPointFontHeader header;
|
||||
CrossPointFontUnicodeInterval* intervals; /// Valid unicode intervals for this font
|
||||
CrossPointFontGlyph* glyphs; /// Glyph array
|
||||
uint8_t* bitmap; /// Glyph bitmaps, concatenated
|
||||
} CrossPointFontData;
|
||||
|
||||
typedef struct {
|
||||
CrossPointFontHeader header;
|
||||
CrossPointFontUnicodeInterval* intervals; /// Valid unicode intervals for this font
|
||||
CrossPointFontSmallGlyph* glyphs; /// Glyph array
|
||||
uint8_t* bitmap; /// Glyph bitmaps, concatenated
|
||||
} CrossPointFontDataSmall;
|
||||
@@ -0,0 +1,51 @@
|
||||
#include "g5enc.inl"
|
||||
#include "g5dec.inl"
|
||||
//
|
||||
// Group5 1-bit image compression library
|
||||
// Written by Larry Bank (bitbank@pobox.com)
|
||||
// Decoder C++ wrapper functions
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2024 BitBank Software, Inc.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
int G5DECODER::init(int iWidth, int iHeight, uint8_t *pData, int iDataSize)
|
||||
{
|
||||
return g5_decode_init(&_g5dec, iWidth, iHeight, pData, iDataSize);
|
||||
} /* init() */
|
||||
|
||||
int G5DECODER::decodeLine(uint8_t *pOut)
|
||||
{
|
||||
return g5_decode_line(&_g5dec, pOut);
|
||||
} /* decodeLine() */
|
||||
|
||||
//
|
||||
// Encoder C++ wrapper functions
|
||||
//
|
||||
int G5ENCODER::init(int iWidth, int iHeight, uint8_t *pOut, int iOutSize)
|
||||
{
|
||||
return g5_encode_init(&_g5enc, iWidth, iHeight, pOut, iOutSize);
|
||||
} /* init() */
|
||||
|
||||
int G5ENCODER::encodeLine(uint8_t *pPixels)
|
||||
{
|
||||
return g5_encode_encodeLine(&_g5enc, pPixels);
|
||||
} /* encodeLine() */
|
||||
|
||||
int G5ENCODER::size()
|
||||
{
|
||||
return g5_encode_getOutSize(&_g5enc);
|
||||
} /* size() */
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
#ifndef __GROUP5__
|
||||
#define __GROUP5__
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#ifdef __AVR__
|
||||
#include <avr/pgmspace.h>
|
||||
#endif
|
||||
//
|
||||
// Group5 1-bit image compression library
|
||||
// Written by Larry Bank (bitbank@pobox.com)
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2024 BitBank Software, Inc.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// The name "Group5" is derived from the CCITT Group4 standard
|
||||
// This code is based on a lot of the good ideas from CCITT T.6
|
||||
// for FAX image compression, but modified to work in a very
|
||||
// constrained environment. The Huffman tables for horizontal
|
||||
// mode have been replaced with a simple 2-bit flag followed by
|
||||
// short or long counts of a fixed length. The short codes are
|
||||
// always 3 bits (run lengths 0-7) and the long codes are the
|
||||
// number of bits needed to encode the width of the image.
|
||||
// For example, if a 320 pixel wide image is being compressed,
|
||||
// the longest horizontal run needed is 320, which requires 9
|
||||
// bits to encode. The 2 prefix bits have the following meaning:
|
||||
// 00 = short, short (3+3 bits)
|
||||
// 01 = short, long (3+N bits)
|
||||
// 10 = long, short (N+3 bits)
|
||||
// 11 = long, long (N+N bits)
|
||||
// The rest of the code works identically to Group4 2D FAX
|
||||
//
|
||||
// Caution - this is the maximum number of color changes per line
|
||||
// The default value is set low to work embedded systems with little RAM
|
||||
// for font compression, this is plenty since each line of a character should have
|
||||
// a maximum of 7 color changes
|
||||
// You can define this in your compiler macros to override the default vlaue
|
||||
//
|
||||
#ifndef MAX_IMAGE_FLIPS
|
||||
#ifdef __AVR__
|
||||
#define MAX_IMAGE_FLIPS 32
|
||||
#else
|
||||
#define MAX_IMAGE_FLIPS 512
|
||||
#endif // __AVR__
|
||||
#endif
|
||||
// Horizontal prefix bits
|
||||
enum {
|
||||
HORIZ_SHORT_SHORT=0,
|
||||
HORIZ_SHORT_LONG,
|
||||
HORIZ_LONG_SHORT,
|
||||
HORIZ_LONG_LONG
|
||||
};
|
||||
|
||||
// Return code for encoder and decoder
|
||||
enum {
|
||||
G5_SUCCESS = 0,
|
||||
G5_INVALID_PARAMETER,
|
||||
G5_DECODE_ERROR,
|
||||
G5_UNSUPPORTED_FEATURE,
|
||||
G5_ENCODE_COMPLETE,
|
||||
G5_DECODE_COMPLETE,
|
||||
G5_NOT_INITIALIZED,
|
||||
G5_DATA_OVERFLOW,
|
||||
G5_MAX_FLIPS_EXCEEDED
|
||||
};
|
||||
//
|
||||
// Decoder state
|
||||
//
|
||||
typedef struct g5_dec_image_tag
|
||||
{
|
||||
int iWidth, iHeight; // image size
|
||||
int iError;
|
||||
int y; // last y value drawn
|
||||
int iVLCSize;
|
||||
int iHLen; // length of 'long' horizontal codes for this image
|
||||
int iPitch; // width in bytes of output buffer
|
||||
uint32_t u32Accum; // fractional scaling accumulator
|
||||
uint32_t ulBitOff, ulBits; // vlc decode variables
|
||||
uint8_t *pSrc, *pBuf; // starting & current buffer pointer
|
||||
int16_t *pCur, *pRef; // current state of current vs reference flips
|
||||
int16_t CurFlips[MAX_IMAGE_FLIPS];
|
||||
int16_t RefFlips[MAX_IMAGE_FLIPS];
|
||||
} G5DECIMAGE;
|
||||
|
||||
// Due to unaligned memory causing an exception, we have to do these macros the slow way
|
||||
#ifdef __AVR__
|
||||
// assume PROGMEM as the source of data
|
||||
inline uint32_t TIFFMOTOLONG(uint8_t *p)
|
||||
{
|
||||
uint32_t u32 = pgm_read_dword(p);
|
||||
return __builtin_bswap32(u32);
|
||||
}
|
||||
#else
|
||||
#define TIFFMOTOLONG(p) (((uint32_t)(*p)<<24UL) + ((uint32_t)(*(p+1))<<16UL) + ((uint32_t)(*(p+2))<<8UL) + (uint32_t)(*(p+3)))
|
||||
#endif // __AVR__
|
||||
|
||||
#define TOP_BIT 0x80000000
|
||||
#define MAX_VALUE 0xffffffff
|
||||
// Must be a 32-bit target processor
|
||||
#define REGISTER_WIDTH 32
|
||||
#define BIGUINT uint32_t
|
||||
|
||||
//
|
||||
// G5 Encoder
|
||||
//
|
||||
|
||||
typedef struct g5_buffered_bits
|
||||
{
|
||||
unsigned char *pBuf; // buffer pointer
|
||||
uint32_t ulBits; // buffered bits
|
||||
uint32_t ulBitOff; // current bit offset
|
||||
uint32_t ulDataSize; // available data
|
||||
} G5_BUFFERED_BITS;
|
||||
|
||||
//
|
||||
// Encoder state
|
||||
//
|
||||
typedef struct g5_enc_image_tag
|
||||
{
|
||||
int iWidth, iHeight; // image size
|
||||
int iError;
|
||||
int y; // last y encoded
|
||||
int iOutSize;
|
||||
int iDataSize; // generated output size
|
||||
uint8_t *pOutBuf;
|
||||
int16_t *pCur, *pRef; // pointers to swap current and reference lines
|
||||
G5_BUFFERED_BITS bb;
|
||||
int16_t CurFlips[MAX_IMAGE_FLIPS];
|
||||
int16_t RefFlips[MAX_IMAGE_FLIPS];
|
||||
} G5ENCIMAGE;
|
||||
|
||||
#ifdef __cplusplus
|
||||
//
|
||||
// The G5 classes wrap portable C code which does the actual work
|
||||
//
|
||||
class G5ENCODER
|
||||
{
|
||||
public:
|
||||
int init(int iWidth, int iHeight, uint8_t *pOut, int iOutSize);
|
||||
int encodeLine(uint8_t *pPixels);
|
||||
int size();
|
||||
|
||||
private:
|
||||
G5ENCIMAGE _g5enc;
|
||||
};
|
||||
class G5DECODER
|
||||
{
|
||||
public:
|
||||
int init(int iWidth, int iHeight, uint8_t *pData, int iDataSize);
|
||||
int decodeLine(uint8_t *pOut);
|
||||
|
||||
private:
|
||||
G5DECIMAGE _g5dec;
|
||||
};
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif // __GROUP5__
|
||||
@@ -0,0 +1,346 @@
|
||||
//
|
||||
// Group5
|
||||
// A 1-bpp image decoder
|
||||
//
|
||||
// Written by Larry Bank (bitbank@pobox.com)
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2024 BitBank Software, Inc.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
#include "Group5.h"
|
||||
|
||||
/*
|
||||
The code tree that follows has: bit_length, decode routine
|
||||
These codes are for Group 4 (MMR) decoding
|
||||
|
||||
01 = vertneg1, 11h = vert1, 20h = horiz, 30h = pass, 12h = vert2
|
||||
02 = vertneg2, 13h = vert3, 03 = vertneg3, 90h = trash
|
||||
*/
|
||||
|
||||
static const uint8_t code_table[128] =
|
||||
{0x90, 0, 0x40, 0, /* trash, uncompr mode - codes 0 and 1 */
|
||||
3, 7, /* V(-3) pos = 2 */
|
||||
0x13, 7, /* V(3) pos = 3 */
|
||||
2, 6, 2, 6, /* V(-2) pos = 4,5 */
|
||||
0x12, 6, 0x12, 6, /* V(2) pos = 6,7 */
|
||||
0x30, 4, 0x30, 4, 0x30, 4, 0x30, 4, /* pass pos = 8->F */
|
||||
0x30, 4, 0x30, 4, 0x30, 4, 0x30, 4,
|
||||
0x20, 3, 0x20, 3, 0x20, 3, 0x20, 3, /* horiz pos = 10->1F */
|
||||
0x20, 3, 0x20, 3, 0x20, 3, 0x20, 3,
|
||||
0x20, 3, 0x20, 3, 0x20, 3, 0x20, 3,
|
||||
0x20, 3, 0x20, 3, 0x20, 3, 0x20, 3,
|
||||
/* V(-1) pos = 20->2F */
|
||||
1, 3, 1, 3, 1, 3, 1, 3,
|
||||
1, 3, 1, 3, 1, 3, 1, 3,
|
||||
1, 3, 1, 3, 1, 3, 1, 3,
|
||||
1, 3, 1, 3, 1, 3, 1, 3,
|
||||
0x11, 3, 0x11, 3, 0x11, 3, 0x11, 3, /* V(1) pos = 30->3F */
|
||||
0x11, 3, 0x11, 3, 0x11, 3, 0x11, 3,
|
||||
0x11, 3, 0x11, 3, 0x11, 3, 0x11, 3,
|
||||
0x11, 3, 0x11, 3, 0x11, 3, 0x11, 3};
|
||||
|
||||
static int g5_decode_init(G5DECIMAGE *pImage, int iWidth, int iHeight, uint8_t *pData, int iDataSize)
|
||||
{
|
||||
if (pImage == NULL || iWidth < 1 || iHeight < 1 || pData == NULL || iDataSize < 1)
|
||||
return G5_INVALID_PARAMETER;
|
||||
|
||||
pImage->iVLCSize = iDataSize;
|
||||
pImage->pSrc = pData;
|
||||
pImage->ulBitOff = 0;
|
||||
pImage->y = 0;
|
||||
pImage->ulBits = TIFFMOTOLONG(pData); // preload the first 32 bits of data
|
||||
pImage->iWidth = iWidth;
|
||||
pImage->iHeight = iHeight;
|
||||
return G5_SUCCESS;
|
||||
|
||||
} /* g5_decode_init() */
|
||||
|
||||
static void G5DrawLine(G5DECIMAGE *pPage, int16_t *pCurFlips, uint8_t *pOut)
|
||||
{
|
||||
int x, len, run;
|
||||
uint8_t lBit, rBit, *p;
|
||||
int iStart = 0, xright = pPage->iWidth;
|
||||
uint8_t *pDest;
|
||||
|
||||
iStart = 0;
|
||||
|
||||
pDest = pOut;
|
||||
len = (xright+7)>>3; // number of bytes to generate
|
||||
for (x=0; x<len; x++) {
|
||||
pOut[x] = 0xff; // start with white and only draw the black runs
|
||||
}
|
||||
x = 0;
|
||||
while (x < xright) { // while the scaled x is within the window bounds
|
||||
x = *pCurFlips++; // black starting point
|
||||
run = *pCurFlips++ - x; // get the black run
|
||||
x -= iStart;
|
||||
if (x >= xright || run == 0)
|
||||
break;
|
||||
if ((x + run) > 0) { /* If the run is visible, draw it */
|
||||
if (x < 0) {
|
||||
run += x; /* draw only visible part of run */
|
||||
x = 0;
|
||||
}
|
||||
if ((x + run) > xright) { /* Don't let it go off right edge */
|
||||
run = xright - x;
|
||||
}
|
||||
/* Draw this run */
|
||||
lBit = 0xff << (8 - (x & 7));
|
||||
rBit = 0xff >> ((x + run) & 7);
|
||||
len = ((x+run)>>3) - (x >> 3);
|
||||
p = &pDest[x >> 3];
|
||||
if (len == 0) {
|
||||
lBit |= rBit;
|
||||
*p &= lBit;
|
||||
} else {
|
||||
*p++ &= lBit;
|
||||
while (len > 1) {
|
||||
*p++ = 0;
|
||||
len--;
|
||||
}
|
||||
*p = rBit;
|
||||
}
|
||||
} // visible run
|
||||
} /* while drawing line */
|
||||
} /* G5DrawLine() */
|
||||
//
|
||||
// Initialize internal structures to decode the image
|
||||
//
|
||||
static void Decode_Begin(G5DECIMAGE *pPage)
|
||||
{
|
||||
int i, xsize;
|
||||
int16_t *CurFlips, *RefFlips;
|
||||
|
||||
xsize = pPage->iWidth;
|
||||
|
||||
RefFlips = pPage->RefFlips;
|
||||
CurFlips = pPage->CurFlips;
|
||||
|
||||
/* Seed the current and reference line with XSIZE for V(0) codes */
|
||||
for (i=0; i<MAX_IMAGE_FLIPS-2; i++) {
|
||||
RefFlips[i] = xsize;
|
||||
CurFlips[i] = xsize;
|
||||
}
|
||||
/* Prefill both current and reference lines with 7fff to prevent it from
|
||||
walking off the end if the data gets bunged and the current X is > XSIZE
|
||||
3-16-94 */
|
||||
CurFlips[i] = RefFlips[i] = 0x7fff;
|
||||
CurFlips[i+1] = RefFlips[i+1] = 0x7fff;
|
||||
|
||||
pPage->pCur = CurFlips;
|
||||
pPage->pRef = RefFlips;
|
||||
pPage->pBuf = pPage->pSrc;
|
||||
pPage->ulBits = TIFFMOTOLONG(pPage->pSrc); // load 32 bits to start
|
||||
pPage->ulBitOff = 0;
|
||||
// Calculate the number of bits needed for a long horizontal code
|
||||
#ifdef __AVR__
|
||||
pPage->iHLen = 16 - __builtin_clz(pPage->iWidth);
|
||||
#else
|
||||
pPage->iHLen = 32 - __builtin_clz(pPage->iWidth);
|
||||
#endif
|
||||
} /* Decode_Begin() */
|
||||
//
|
||||
// Decode a single line of G5 data (private function)
|
||||
//
|
||||
static int DecodeLine(G5DECIMAGE *pPage)
|
||||
{
|
||||
signed int a0, a0_p, b1;
|
||||
int16_t *pCur, *pRef, *RefFlips, *CurFlips;
|
||||
int xsize, tot_run=0, tot_run1 = 0;
|
||||
int32_t sCode;
|
||||
uint32_t lBits;
|
||||
uint32_t ulBits, ulBitOff;
|
||||
uint8_t *pBuf/*, *pBufEnd*/;
|
||||
uint32_t u32HMask, u32HLen; // horizontal code mask and length
|
||||
|
||||
pCur = CurFlips = pPage->pCur;
|
||||
pRef = RefFlips = pPage->pRef;
|
||||
ulBits = pPage->ulBits;
|
||||
ulBitOff = pPage->ulBitOff;
|
||||
pBuf = pPage->pBuf;
|
||||
// pBufEnd = &pPage->pSrc[pPage->iVLCSize];
|
||||
u32HLen = pPage->iHLen;
|
||||
u32HMask = (1 << u32HLen) - 1;
|
||||
a0 = -1;
|
||||
xsize = pPage->iWidth;
|
||||
|
||||
while (a0 < xsize) { /* Decode this line */
|
||||
if (ulBitOff > (REGISTER_WIDTH-8)) { // need at least 7 unused bits
|
||||
pBuf += (ulBitOff >> 3);
|
||||
ulBitOff &= 7;
|
||||
ulBits = TIFFMOTOLONG(pBuf);
|
||||
}
|
||||
if ((int32_t)(ulBits << ulBitOff) < 0) { /* V(0) code is the most frequent case (1 bit) */
|
||||
a0 = *pRef++;
|
||||
ulBitOff++; // length = 1 bit
|
||||
*pCur++ = a0;
|
||||
} else { /* Slower method for the less frequence codes */
|
||||
lBits = (ulBits >> ((REGISTER_WIDTH - 8) - ulBitOff)) & 0xfe; /* Only the first 7 bits are useful */
|
||||
sCode = code_table[lBits]; /* Get the code type as an 8-bit value */
|
||||
ulBitOff += code_table[lBits+1]; /* Get the code length */
|
||||
switch (sCode) {
|
||||
case 1: /* V(-1) */
|
||||
case 2: /* V(-2) */
|
||||
case 3: /* V(-3) */
|
||||
a0 = *pRef - sCode; /* A0 = B1 - x */
|
||||
*pCur++ = a0;
|
||||
if (pRef == RefFlips) {
|
||||
pRef += 2;
|
||||
}
|
||||
pRef--;
|
||||
while (a0 >= *pRef) {
|
||||
pRef += 2;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x11: /* V(1) */
|
||||
case 0x12: /* V(2) */
|
||||
case 0x13: /* V(3) */
|
||||
a0 = *pRef++; /* A0 = B1 */
|
||||
b1 = a0;
|
||||
a0 += sCode & 7; /* A0 = B1 + x */
|
||||
if (b1 != xsize && a0 < xsize) {
|
||||
while (a0 >= *pRef) {
|
||||
pRef += 2;
|
||||
}
|
||||
}
|
||||
if (a0 > xsize) {
|
||||
a0 = xsize;
|
||||
}
|
||||
*pCur++ = a0;
|
||||
break;
|
||||
|
||||
case 0x20: /* Horizontal codes */
|
||||
if (ulBitOff > (REGISTER_WIDTH-16)) { // need at least 16 unused bits
|
||||
pBuf += (ulBitOff >> 3);
|
||||
ulBitOff &= 7;
|
||||
ulBits = TIFFMOTOLONG(pBuf);
|
||||
}
|
||||
a0_p = a0;
|
||||
if (a0 < 0) {
|
||||
a0_p = 0;
|
||||
}
|
||||
lBits = (ulBits >> ((REGISTER_WIDTH - 2) - ulBitOff)) & 0x3; // get 2-bit prefix for code type
|
||||
// There are 4 possible horizontal cases: short/short, short/long, long/short, long/long
|
||||
// These are encoded in a 2-bit prefix code, followed by 3 bits for short or N bits for long code
|
||||
// N is the log base 2 of the image width (e.g. 320 pixels requires 9 bits)
|
||||
ulBitOff += 2;
|
||||
switch (lBits) {
|
||||
case HORIZ_SHORT_SHORT:
|
||||
tot_run = (ulBits >> ((REGISTER_WIDTH - 3) - ulBitOff)) & 0x7; // get 3-bit short length
|
||||
ulBitOff += 3;
|
||||
tot_run1 = (ulBits >> ((REGISTER_WIDTH - 3) - ulBitOff)) & 0x7; // get 3-bit short length
|
||||
ulBitOff += 3;
|
||||
break;
|
||||
case HORIZ_SHORT_LONG:
|
||||
tot_run = (ulBits >> ((REGISTER_WIDTH - 3) - ulBitOff)) & 0x7; // get 3-bit short length
|
||||
ulBitOff += 3;
|
||||
tot_run1 = (ulBits >> ((REGISTER_WIDTH - u32HLen) - ulBitOff)) & u32HMask; // get long length
|
||||
ulBitOff += u32HLen;
|
||||
break;
|
||||
case HORIZ_LONG_SHORT:
|
||||
tot_run = (ulBits >> ((REGISTER_WIDTH - u32HLen) - ulBitOff)) & u32HMask; // get long length
|
||||
ulBitOff += u32HLen;
|
||||
tot_run1 = (ulBits >> ((REGISTER_WIDTH - 3) - ulBitOff)) & 0x7; // get 3-bit short length
|
||||
ulBitOff += 3;
|
||||
break;
|
||||
case HORIZ_LONG_LONG:
|
||||
tot_run = (ulBits >> ((REGISTER_WIDTH - u32HLen) - ulBitOff)) & u32HMask; // get long length
|
||||
ulBitOff += u32HLen;
|
||||
if (ulBitOff > (REGISTER_WIDTH-16)) { // need at least 16 unused bits
|
||||
pBuf += (ulBitOff >> 3);
|
||||
ulBitOff &= 7;
|
||||
ulBits = TIFFMOTOLONG(pBuf);
|
||||
}
|
||||
tot_run1 = (ulBits >> ((REGISTER_WIDTH - u32HLen) - ulBitOff)) & u32HMask; // get long length
|
||||
ulBitOff += u32HLen;
|
||||
break;
|
||||
} // switch on lBits
|
||||
a0 = a0_p + tot_run;
|
||||
*pCur++ = a0;
|
||||
a0 += tot_run1;
|
||||
if (a0 < xsize) {
|
||||
while (a0 >= *pRef) {
|
||||
pRef += 2;
|
||||
}
|
||||
}
|
||||
*pCur++ = a0;
|
||||
break;
|
||||
|
||||
case 0x30: /* Pass code */
|
||||
pRef++; /* A0 = B2, iRef+=2 */
|
||||
a0 = *pRef++;
|
||||
break;
|
||||
|
||||
default: /* ERROR */
|
||||
pPage->iError = G5_DECODE_ERROR;
|
||||
goto pilreadg5z;
|
||||
} /* switch */
|
||||
} /* Slow climb */
|
||||
}
|
||||
/*--- Convert flips data into run lengths ---*/
|
||||
*pCur++ = xsize; /* Terminate the line properly */
|
||||
*pCur++ = xsize;
|
||||
pilreadg5z:
|
||||
// Save the current VLC decoder state
|
||||
pPage->ulBits = ulBits;
|
||||
pPage->ulBitOff = ulBitOff;
|
||||
pPage->pBuf = pBuf;
|
||||
return pPage->iError;
|
||||
} /* DecodeLine() */
|
||||
//
|
||||
// Decompress the VLC data
|
||||
//
|
||||
static int g5_decode_line(G5DECIMAGE *pPage, uint8_t *pOut)
|
||||
{
|
||||
int rc;
|
||||
uint8_t *pBufEnd;
|
||||
int16_t *t1;
|
||||
|
||||
if (pPage == NULL || pOut == NULL)
|
||||
return G5_INVALID_PARAMETER;
|
||||
if (pPage->y >= pPage->iHeight)
|
||||
return G5_DECODE_COMPLETE;
|
||||
|
||||
pPage->iError = G5_SUCCESS;
|
||||
|
||||
if (pPage->y == 0) { // first time through
|
||||
Decode_Begin(pPage);
|
||||
}
|
||||
pBufEnd = &pPage->pSrc[pPage->iVLCSize];
|
||||
|
||||
if (pPage->pBuf >= pBufEnd) { // read past the end, error
|
||||
pPage->iError = G5_DECODE_ERROR;
|
||||
return G5_DECODE_ERROR;
|
||||
}
|
||||
rc = DecodeLine(pPage);
|
||||
if (rc == G5_SUCCESS) {
|
||||
// Draw the current line
|
||||
G5DrawLine(pPage, pPage->pCur, pOut);
|
||||
/*--- Swap current and reference lines ---*/
|
||||
t1 = pPage->pRef;
|
||||
pPage->pRef = pPage->pCur;
|
||||
pPage->pCur = t1;
|
||||
pPage->y++;
|
||||
if (pPage->y >= pPage->iHeight) {
|
||||
pPage->iError = G5_DECODE_COMPLETE;
|
||||
}
|
||||
} else {
|
||||
pPage->iError = rc;
|
||||
}
|
||||
return pPage->iError;
|
||||
} /* Decode() */
|
||||
@@ -0,0 +1,315 @@
|
||||
//
|
||||
// G5 Encoder
|
||||
// A 1-bpp image encoding library
|
||||
//
|
||||
// Written by Larry Bank (bitbank@pobox.com)
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2024 BitBank Software, Inc.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
#include "Group5.h"
|
||||
|
||||
/* Number of consecutive 1 bits in a byte from MSB to LSB */
|
||||
static uint8_t bitcount[256] =
|
||||
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0-15 */
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 16-31 */
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 32-47 */
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 48-63 */
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 64-79 */
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 80-95 */
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 96-111 */
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 112-127 */
|
||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 128-143 */
|
||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 144-159 */
|
||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 160-175 */
|
||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 176-191 */
|
||||
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, /* 192-207 */
|
||||
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, /* 208-223 */
|
||||
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, /* 224-239 */
|
||||
4,4,4,4,4,4,4,4,5,5,5,5,6,6,7,8}; /* 240-255 */
|
||||
|
||||
/* Table of vertical codes for G5 encoding */
|
||||
/* code followed by length, starting with v(-3) */
|
||||
static const uint8_t vtable[14] =
|
||||
{3,7, /* V(-3) = 0000011 */
|
||||
3,6, /* V(-2) = 000011 */
|
||||
3,3, /* V(-1) = 011 */
|
||||
1,1, /* V(0) = 1 */
|
||||
2,3, /* V(1) = 010 */
|
||||
2,6, /* V(2) = 000010 */
|
||||
2,7}; /* V(3) = 0000010 */
|
||||
|
||||
|
||||
static void G5ENCInsertCode(G5_BUFFERED_BITS *bb, BIGUINT ulCode, int iLen)
|
||||
{
|
||||
if ((bb->ulBitOff + iLen) > REGISTER_WIDTH) { // need to write data
|
||||
bb->ulBits |= (ulCode >> (bb->ulBitOff + iLen - REGISTER_WIDTH)); // partial bits on first word
|
||||
*(BIGUINT *)bb->pBuf = __builtin_bswap32(bb->ulBits);
|
||||
bb->pBuf += sizeof(BIGUINT);
|
||||
bb->ulBits = ulCode << ((REGISTER_WIDTH*2) - (bb->ulBitOff + iLen));
|
||||
bb->ulBitOff += iLen - REGISTER_WIDTH;
|
||||
} else {
|
||||
bb->ulBits |= (ulCode << (REGISTER_WIDTH - bb->ulBitOff - iLen));
|
||||
bb->ulBitOff += iLen;
|
||||
}
|
||||
} /* G5ENCInsertCode() */
|
||||
//
|
||||
// Flush any buffered bits to the output
|
||||
//
|
||||
static void G5ENCFlushBits(G5_BUFFERED_BITS *bb)
|
||||
{
|
||||
while (bb->ulBitOff >= 8)
|
||||
{
|
||||
*bb->pBuf++ = (unsigned char) (bb->ulBits >> (REGISTER_WIDTH - 8));
|
||||
bb->ulBits <<= 8;
|
||||
bb->ulBitOff -= 8;
|
||||
}
|
||||
if (bb->ulBitOff) { // partial byte?
|
||||
*bb->pBuf++ = (unsigned char) (bb->ulBits >> (REGISTER_WIDTH - 8));
|
||||
}
|
||||
bb->ulBitOff = 0;
|
||||
bb->ulBits = 0;
|
||||
} /* G5ENCFlushBits() */
|
||||
//
|
||||
// Initialize the compressor
|
||||
// This must be called before adding data to the output
|
||||
//
|
||||
static int g5_encode_init(G5ENCIMAGE *pImage, int iWidth, int iHeight, uint8_t *pOut, int iOutSize)
|
||||
{
|
||||
int iError = G5_SUCCESS;
|
||||
|
||||
if (pImage == NULL || iHeight <= 0)
|
||||
return G5_INVALID_PARAMETER;
|
||||
pImage->iWidth = iWidth; // image size
|
||||
pImage->iHeight = iHeight;
|
||||
pImage->pCur = pImage->CurFlips;
|
||||
pImage->pRef = pImage->RefFlips;
|
||||
pImage->pOutBuf = pOut; // optional output buffer
|
||||
pImage->iOutSize = iOutSize; // output buffer pre-allocated size
|
||||
pImage->iDataSize = 0; // no data yet
|
||||
pImage->y = 0;
|
||||
for (int i=0; i<MAX_IMAGE_FLIPS; i++) {
|
||||
pImage->RefFlips[i] = iWidth;
|
||||
pImage->CurFlips[i] = iWidth;
|
||||
}
|
||||
pImage->bb.pBuf = pImage->pOutBuf;
|
||||
pImage->bb.ulBits = 0;
|
||||
pImage->bb.ulBitOff = 0;
|
||||
pImage->iError = iError;
|
||||
return iError;
|
||||
} /* g5_encode_init() */
|
||||
//
|
||||
// Internal function to convert uncompressed 1-bit per pixel data
|
||||
// into the run-end data needed to feed the G5 encoder
|
||||
//
|
||||
static int G5ENCEncodeLine(unsigned char *buf, int xsize, int16_t *pDest)
|
||||
{
|
||||
int iCount, xborder;
|
||||
uint8_t i, c;
|
||||
int8_t cBits;
|
||||
int iLen;
|
||||
int16_t x;
|
||||
int16_t *pLimit = pDest + (MAX_IMAGE_FLIPS-4);
|
||||
|
||||
xborder = xsize;
|
||||
iCount = (xsize + 7) >> 3; /* Number of bytes per line */
|
||||
cBits = 8;
|
||||
iLen = 0; /* Current run length */
|
||||
x = 0;
|
||||
|
||||
c = *buf++; /* Get the first byte to start */
|
||||
iCount--;
|
||||
while (iCount >=0) {
|
||||
if (pDest >= pLimit) return G5_MAX_FLIPS_EXCEEDED;
|
||||
i = bitcount[c]; /* Get the number of consecutive bits */
|
||||
iLen += i; /* Add this length to total run length */
|
||||
c <<= i;
|
||||
cBits -= i; /* Minus the number in a byte */
|
||||
if (cBits <= 0)
|
||||
{
|
||||
iLen += cBits; /* Adjust length */
|
||||
cBits = 8;
|
||||
c = *buf++; /* Get another data byte */
|
||||
iCount--;
|
||||
continue; /* Keep doing white until color change */
|
||||
}
|
||||
c = ~c; /* flip color to count black pixels */
|
||||
/* Store the white run length */
|
||||
xborder -= iLen;
|
||||
if (xborder < 0)
|
||||
{
|
||||
iLen += xborder; /* Make sure run length is not past end */
|
||||
break;
|
||||
}
|
||||
x += iLen;
|
||||
*pDest++ = x;
|
||||
iLen = 0;
|
||||
doblack:
|
||||
i = bitcount[c]; /* Get consecutive bits */
|
||||
iLen += i; /* Add to total run length */
|
||||
c <<= i;
|
||||
cBits -= i;
|
||||
if (cBits <= 0)
|
||||
{
|
||||
iLen += cBits; /* Adjust length */
|
||||
cBits = 8;
|
||||
c = *buf++; /* Get another data byte */
|
||||
c = ~c; /* Flip color to find black */
|
||||
iCount--;
|
||||
if (iCount < 0)
|
||||
break;
|
||||
goto doblack;
|
||||
}
|
||||
/* Store the black run length */
|
||||
c = ~c; /* Flip color again to find white pixels */
|
||||
xborder -= iLen;
|
||||
if (xborder < 0)
|
||||
{
|
||||
iLen += xborder; /* Make sure run length is not past end */
|
||||
break;
|
||||
}
|
||||
x += iLen;
|
||||
*pDest++ = x;
|
||||
iLen = 0;
|
||||
} /* while */
|
||||
|
||||
if (pDest >= pLimit) return G5_MAX_FLIPS_EXCEEDED;
|
||||
*pDest++ = xsize;
|
||||
*pDest++ = xsize; // Store a few more XSIZE to end the line
|
||||
*pDest++ = xsize; // so that the compressor doesn't go past
|
||||
*pDest++ = xsize; // the end of the line
|
||||
return G5_SUCCESS;
|
||||
} /* G5ENCEncodeLine() */
|
||||
//
|
||||
// Compress a line of pixels and add it to the output
|
||||
// the input format is expected to be MSB (most significant bit) first
|
||||
// for example, pixel 0 is in byte 0 at bit 7 (0x80)
|
||||
// Returns G5ENC_SUCCESS for each line if all is well and G5ENC_IMAGE_COMPLETE
|
||||
// for the last line
|
||||
//
|
||||
static int g5_encode_encodeLine(G5ENCIMAGE *pImage, uint8_t *pPixels)
|
||||
{
|
||||
int16_t a0, a0_c, b2, a1;
|
||||
int dx, run1, run2;
|
||||
int xsize, iErr, iHighWater;
|
||||
int iCur, iRef, iLen;
|
||||
int iHLen; // number of bits for long horizontal codes
|
||||
int16_t *CurFlips, *RefFlips;
|
||||
G5_BUFFERED_BITS bb;
|
||||
|
||||
if (pImage == NULL || pPixels == NULL)
|
||||
return G5_INVALID_PARAMETER;
|
||||
iHighWater = pImage->iOutSize - 32;
|
||||
iHLen = 32 - __builtin_clz(pImage->iWidth);
|
||||
memcpy(&bb, &pImage->bb, sizeof(G5_BUFFERED_BITS)); // keep local copy
|
||||
CurFlips = pImage->pCur;
|
||||
RefFlips = pImage->pRef;
|
||||
xsize = pImage->iWidth; /* For performance reasons */
|
||||
|
||||
// Convert the incoming line of pixels into run-end data
|
||||
iErr = G5ENCEncodeLine(pPixels, pImage->iWidth, CurFlips);
|
||||
if (iErr != G5_SUCCESS) return iErr; // exceeded the maximum number of color changes
|
||||
/* Encode this line as G5 */
|
||||
a0 = a0_c = 0;
|
||||
iCur = iRef = 0;
|
||||
while (a0 < xsize) {
|
||||
b2 = RefFlips[iRef+1];
|
||||
a1 = CurFlips[iCur];
|
||||
if (b2 < a1) { /* Is b2 to the left of a1? */
|
||||
/* yes, do pass mode */
|
||||
a0 = b2;
|
||||
iRef += 2;
|
||||
G5ENCInsertCode(&bb, 1, 4); /* Pass code = 0001 */
|
||||
} else { /* Try vertical and horizontal mode */
|
||||
dx = RefFlips[iRef] - a1; /* b1 - a1 */
|
||||
if (dx > 3 || dx < -3) { /* Horizontal mode */
|
||||
G5ENCInsertCode(&bb, 1, 3); /* Horizontal code = 001 */
|
||||
run1 = CurFlips[iCur] - a0;
|
||||
run2 = CurFlips[iCur+1] - CurFlips[iCur];
|
||||
if (run1 < 8) {
|
||||
if (run2 < 8) { // short, short
|
||||
G5ENCInsertCode(&bb, HORIZ_SHORT_SHORT, 2); /* short, short = 00 */
|
||||
G5ENCInsertCode(&bb, run1, 3);
|
||||
G5ENCInsertCode(&bb, run2, 3);
|
||||
} else { // short, long
|
||||
G5ENCInsertCode(&bb, HORIZ_SHORT_LONG, 2); /* short, long = 01 */
|
||||
G5ENCInsertCode(&bb, run1, 3);
|
||||
G5ENCInsertCode(&bb, run2, iHLen);
|
||||
}
|
||||
} else { // first run is long
|
||||
if (run2 < 8) { // long, short
|
||||
G5ENCInsertCode(&bb, HORIZ_LONG_SHORT, 2); /* long, short = 10 */
|
||||
G5ENCInsertCode(&bb, run1, iHLen);
|
||||
G5ENCInsertCode(&bb, run2, 3);
|
||||
} else { // long, long
|
||||
G5ENCInsertCode(&bb, HORIZ_LONG_LONG, 2); /* long, long = 11 */
|
||||
G5ENCInsertCode(&bb, run1, iHLen);
|
||||
G5ENCInsertCode(&bb, run2, iHLen);
|
||||
}
|
||||
}
|
||||
a0 = CurFlips[iCur+1]; /* a0 = a2 */
|
||||
if (a0 != xsize) {
|
||||
iCur += 2; /* Skip two color flips */
|
||||
while (RefFlips[iRef] != xsize && RefFlips[iRef] <= a0) {
|
||||
iRef += 2;
|
||||
}
|
||||
}
|
||||
} else { /* Vertical mode */
|
||||
dx = (dx + 3) * 2; /* Convert to index table */
|
||||
G5ENCInsertCode(&bb, vtable[dx], vtable[dx+1]);
|
||||
a0 = a1;
|
||||
a0_c = 1-a0_c;
|
||||
if (a0 != xsize) {
|
||||
if (iRef != 0) {
|
||||
iRef -= 2;
|
||||
}
|
||||
iRef++; /* Skip a color change in cur and ref */
|
||||
iCur++;
|
||||
while (RefFlips[iRef] <= a0 && RefFlips[iRef] != xsize) {
|
||||
iRef += 2;
|
||||
}
|
||||
}
|
||||
} /* vertical mode */
|
||||
} /* horiz/vert mode */
|
||||
} /* while x < xsize */
|
||||
iLen = (int)(bb.pBuf-pImage->pOutBuf);
|
||||
if (iLen >= iHighWater) { // not enough space
|
||||
pImage->iError = iErr = G5_DATA_OVERFLOW; // we don't have a better error
|
||||
return iErr;
|
||||
}
|
||||
if (pImage->y == pImage->iHeight-1) { // last line of image
|
||||
G5ENCFlushBits(&bb); // output the final buffered bits
|
||||
// wrap up final output
|
||||
pImage->iDataSize = 1 + (int)(bb.pBuf-pImage->pOutBuf);
|
||||
iErr = G5_ENCODE_COMPLETE;
|
||||
}
|
||||
pImage->pCur = RefFlips; // swap current and reference lines
|
||||
pImage->pRef = CurFlips;
|
||||
pImage->y++;
|
||||
memcpy(&pImage->bb, &bb, sizeof(bb));
|
||||
return iErr;
|
||||
} /* g5_encode_encodeLine() */
|
||||
//
|
||||
// Returns the number of bytes of G5 created by the encoder
|
||||
//
|
||||
static int g5_encode_getOutSize(G5ENCIMAGE *pImage)
|
||||
{
|
||||
int iSize = 0;
|
||||
if (pImage != NULL)
|
||||
iSize = pImage->iDataSize;
|
||||
return iSize;
|
||||
} /* g5_encode_getOutSize() */
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <builtinFonts/bookerly_12.h>
|
||||
#include <builtinFonts/bookerly_14.h>
|
||||
#include <builtinFonts/bookerly_16.h>
|
||||
#include <builtinFonts/bookerly_18.h>
|
||||
#include <builtinFonts/notosans_8.h>
|
||||
#include <builtinFonts/notosans_12.h>
|
||||
#include <builtinFonts/notosans_14.h>
|
||||
#include <builtinFonts/notosans_16.h>
|
||||
#include <builtinFonts/notosans_18.h>
|
||||
#include <builtinFonts/opendyslexic_8.h>
|
||||
#include <builtinFonts/opendyslexic_10.h>
|
||||
#include <builtinFonts/opendyslexic_12.h>
|
||||
#include <builtinFonts/opendyslexic_14.h>
|
||||
#include <builtinFonts/ubuntu_10.h>
|
||||
#include <builtinFonts/ubuntu_12.h>
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+25
@@ -0,0 +1,25 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
echo "// The contents of this file are generated by ./lib/CrossPointFont/builtinFonts/build-font-ids.sh"
|
||||
echo "#pragma once"
|
||||
echo ""
|
||||
|
||||
echo "#define BOOKERLY_12_FONT_ID ($(ruby -rdigest -e 'puts Digest::SHA256.hexdigest(File.read("./bookerly_12.h")).to_i(16) % (2 ** 32) - (2 ** 31)'))"
|
||||
echo "#define BOOKERLY_14_FONT_ID ($(ruby -rdigest -e 'puts Digest::SHA256.hexdigest(File.read("./bookerly_14.h")).to_i(16) % (2 ** 32) - (2 ** 31)'))"
|
||||
echo "#define BOOKERLY_16_FONT_ID ($(ruby -rdigest -e 'puts Digest::SHA256.hexdigest(File.read("./bookerly_16.h")).to_i(16) % (2 ** 32) - (2 ** 31)'))"
|
||||
echo "#define BOOKERLY_18_FONT_ID ($(ruby -rdigest -e 'puts Digest::SHA256.hexdigest(File.read("./bookerly_18.h")).to_i(16) % (2 ** 32) - (2 ** 31)'))"
|
||||
echo "#define NOTOSANS_12_FONT_ID ($(ruby -rdigest -e 'puts Digest::SHA256.hexdigest(File.read("./notosans_12.h")).to_i(16) % (2 ** 32) - (2 ** 31)'))"
|
||||
echo "#define NOTOSANS_14_FONT_ID ($(ruby -rdigest -e 'puts Digest::SHA256.hexdigest(File.read("./notosans_14.h")).to_i(16) % (2 ** 32) - (2 ** 31)'))"
|
||||
echo "#define NOTOSANS_16_FONT_ID ($(ruby -rdigest -e 'puts Digest::SHA256.hexdigest(File.read("./notosans_16.h")).to_i(16) % (2 ** 32) - (2 ** 31)'))"
|
||||
echo "#define NOTOSANS_18_FONT_ID ($(ruby -rdigest -e 'puts Digest::SHA256.hexdigest(File.read("./notosans_18.h")).to_i(16) % (2 ** 32) - (2 ** 31)'))"
|
||||
echo "#define OPENDYSLEXIC_8_FONT_ID ($(ruby -rdigest -e 'puts Digest::SHA256.hexdigest(File.read("./opendyslexic_8.h")).to_i(16) % (2 ** 32) - (2 ** 31)'))"
|
||||
echo "#define OPENDYSLEXIC_10_FONT_ID ($(ruby -rdigest -e 'puts Digest::SHA256.hexdigest(File.read("./opendyslexic_10.h")).to_i(16) % (2 ** 32) - (2 ** 31)'))"
|
||||
echo "#define OPENDYSLEXIC_12_FONT_ID ($(ruby -rdigest -e 'puts Digest::SHA256.hexdigest(File.read("./opendyslexic_12.h")).to_i(16) % (2 ** 32) - (2 ** 31)'))"
|
||||
echo "#define OPENDYSLEXIC_14_FONT_ID ($(ruby -rdigest -e 'puts Digest::SHA256.hexdigest(File.read("./opendyslexic_14.h")).to_i(16) % (2 ** 32) - (2 ** 31)'))"
|
||||
echo "#define UI_10_FONT_ID ($(ruby -rdigest -e 'puts Digest::SHA256.hexdigest(File.read("./ubuntu_10.h")).to_i(16) % (2 ** 32) - (2 ** 31)'))"
|
||||
echo "#define UI_12_FONT_ID ($(ruby -rdigest -e 'puts Digest::SHA256.hexdigest(File.read("./ubuntu_12.h")).to_i(16) % (2 ** 32) - (2 ** 31)'))"
|
||||
echo "#define SMALL_FONT_ID ($(ruby -rdigest -e 'puts Digest::SHA256.hexdigest(File.read("./notosans_8.h")).to_i(16) % (2 ** 32) - (2 ** 31)'))"
|
||||
@@ -0,0 +1,42 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
BOOKERLY_FONT_SIZES=(12 14 16 18)
|
||||
NOTOSANS_FONT_SIZES=(12 14 16 18)
|
||||
OPENDYSLEXIC_FONT_SIZES=(8 10 12 14)
|
||||
|
||||
for size in ${BOOKERLY_FONT_SIZES[@]}; do
|
||||
font_name="bookerly_${size}"
|
||||
font_path_prefix="./source/Bookerly/Bookerly-"
|
||||
output_path="./${font_name}.h"
|
||||
../fontconvert/fontconvert "${font_path_prefix}Regular.ttf" -b "${font_path_prefix}Bold.ttf" -i "${font_path_prefix}Italic.ttf" -bi "${font_path_prefix}BoldItalic.ttf" -o $output_path -p $size
|
||||
done
|
||||
|
||||
for size in ${NOTOSANS_FONT_SIZES[@]}; do
|
||||
font_name="notosans_${size}"
|
||||
font_path_prefix="./source/NotoSans/NotoSans-"
|
||||
output_path="./${font_name}.h"
|
||||
../fontconvert/fontconvert "${font_path_prefix}Regular.ttf" -b "${font_path_prefix}Bold.ttf" -i "${font_path_prefix}Italic.ttf" -bi "${font_path_prefix}BoldItalic.ttf" -o $output_path -p $size
|
||||
done
|
||||
|
||||
for size in ${OPENDYSLEXIC_FONT_SIZES[@]}; do
|
||||
font_name="opendyslexic_${size}"
|
||||
font_path_prefix="./source/OpenDyslexic/OpenDyslexic-"
|
||||
output_path="./${font_name}.h"
|
||||
../fontconvert/fontconvert "${font_path_prefix}Regular.otf" -b "${font_path_prefix}Bold.otf" -i "${font_path_prefix}Italic.otf" -bi "${font_path_prefix}BoldItalic.otf" -o $output_path -p $size
|
||||
done
|
||||
|
||||
UI_FONT_SIZES=(10 12)
|
||||
UI_FONT_STYLES=("Regular" "Bold")
|
||||
|
||||
for size in ${UI_FONT_SIZES[@]}; do
|
||||
font_name="ubuntu_${size}"
|
||||
font_path_prefix="./source/Ubuntu/Ubuntu-"
|
||||
output_path="./${font_name}.h"
|
||||
../fontconvert/fontconvert "${font_path_prefix}Regular.ttf" -b "${font_path_prefix}Bold.ttf" -o $output_path -p $size
|
||||
done
|
||||
|
||||
../fontconvert/fontconvert ./source/NotoSans/NotoSans-Regular.ttf -o ./notosans_8.h -p 8
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user