Skip to content

Commit

Permalink
Add 16-bit support, and related clean-ups
Browse files Browse the repository at this point in the history
  • Loading branch information
attah committed Nov 29, 2024
1 parent 23b3f05 commit 28fbb9a
Show file tree
Hide file tree
Showing 10 changed files with 320 additions and 150 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
.vscode
*.o
*.plist
tests/test
tests/*.o
tests/_test_pdf2printable_16x9_*
Expand Down
6 changes: 2 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@
Available as rudimentary standalone applications, but mainly made for use in [SeaPrint](https://github.com/attah/harbour-seaprint).

## ppm2pwg
Takes a pbm, pgm or ppm (P4, P5 or P6 "raw") Netpbm bitmap image and converts to PWG or URF printer raster format.

...or a raw bitmap in the c++ api.
Takes a pbm, pgm or ppm (P4, P5 or P6 "raw") Netpbm bitmap image and converts to PWG or URF printer raster format. Supports 1, 8 and **16** bits per color.

## pwg2ppm
For debugging. Similar to [rasterview](https://github.com/michaelrsweet/rasterview), but without a GUI. Takes a PWG or URF printer raster and outputs a series of P4, P5 or P6 pbm/pgm/ppm images.
Expand All @@ -24,7 +22,7 @@ Takes a PDF document and makes it suitable for printing, by:

## baselinify
Takes a JPEG and losslessly repacks it to the baseline ecoding profile, keeping only JFIF and Exif headers.
Sort of like jpegtran without any arguments, but reusable in C++.
Sort of like jpegtran without any arguments.

IPP-printers are only required to support baseline-encoded jpeg according to PWG5100.14.

Expand Down
4 changes: 4 additions & 0 deletions lib/pdf2printable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,10 @@ void copy_raster_buffer(Bytestream& bmpBts, uint32_t* data, const PrintParameter
}
break;
}
default:
{
throw std::logic_error("Unhandled color mode");
}
}
}

Expand Down
76 changes: 47 additions & 29 deletions lib/ppm2pwg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@
#include <map>
#include <string.h>

void make_pwg_hdr(Bytestream& outBts, const PrintParameters& params, bool backside);
void make_urf_hdr(Bytestream& outBts, const PrintParameters& params);

void compress_lines(Bytestream& bmpBts, Bytestream& outBts, const PrintParameters& params, bool backside);
void compress_line(uint8_t* raw, size_t len, Bytestream& outBts, size_t oneChunk);

Bytestream make_pwg_file_hdr()
{
Bytestream pwgFileHdr;
Expand Down Expand Up @@ -42,21 +48,27 @@ void bmp_to_pwg(Bytestream& bmpBts, Bytestream& outBts, size_t page, const Print
size_t yRes = params.getPaperSizeHInPixels();
uint8_t* raw = bmpBts.raw();
size_t bytesPerLine = params.getPaperSizeWInBytes();
int step = backside&&params.getBackVFlip() ? -bytesPerLine : bytesPerLine;
uint8_t* row0 = backside&&params.getBackVFlip() ? raw+(yRes-1)*bytesPerLine : raw;
int oneLine = backside && params.getBackVFlip() ? -bytesPerLine : bytesPerLine;
uint8_t* row0 = backside && params.getBackVFlip() ? raw + (yRes - 1) * bytesPerLine : raw;
Array<uint8_t> tmpLine(bytesPerLine);

for(size_t y=0; y<yRes; y++)
size_t colors = params.getNumberOfColors();
size_t bpc = params.getBitsPerColor();
// A chunk is the unit used for compression.
// Usually this is the number of bytes per color times the number of colors,
// but for 1-bit, compression is applied in whole bytes.
size_t oneChunk = bpc == 1 ? colors : colors * bpc / 8;

for(size_t y = 0; y < yRes; y++)
{
uint8_t* thisLine = row0+y*step;
uint8_t* thisLine = row0 + y * oneLine;
uint8_t lineRepeat = 0;
size_t colors = params.getNumberOfColors();

uint8_t* next_line = thisLine + step;
uint8_t* next_line = thisLine + oneLine;
while((y+1)<yRes && memcmp(thisLine, next_line, bytesPerLine) == 0)
{
y++;
next_line += step;
next_line += oneLine;
lineRepeat++;
if(lineRepeat == 255)
{
Expand All @@ -68,7 +80,7 @@ void bmp_to_pwg(Bytestream& bmpBts, Bytestream& outBts, size_t page, const Print
if(backside && params.getBackHFlip())
{
// Flip line into tmp buffer
if(params.getBitsPerColor() == 1)
if(bpc == 1)
{
for(size_t i = 0; i < bytesPerLine; i++)
{
Expand All @@ -78,46 +90,48 @@ void bmp_to_pwg(Bytestream& bmpBts, Bytestream& outBts, size_t page, const Print
}
else
{
for(size_t i = 0; i < bytesPerLine; i += colors)
uint8_t* lastChunk = thisLine + bytesPerLine - oneChunk;
for(size_t i = 0; i < bytesPerLine; i += oneChunk)
{
memcpy(tmpLine+i, thisLine+bytesPerLine-colors-i, colors);
memcpy(tmpLine+i, lastChunk-i, oneChunk);
}
}
compress_line(tmpLine, bytesPerLine, outBts, colors);
compress_line(tmpLine, bytesPerLine, outBts, oneChunk);
}
else
{
compress_line(thisLine, bytesPerLine, outBts, colors);
compress_line(thisLine, bytesPerLine, outBts, oneChunk);
}
}
}

void compress_line(uint8_t* raw, size_t len, Bytestream& outBts, int colors)
void compress_line(uint8_t* raw, size_t len, Bytestream& outBts, size_t oneChunk)
{
uint8_t* current;
uint8_t* pos = raw;
uint8_t* epos = raw+len;
uint8_t* epos = raw + len;

while(pos != epos)
{
uint8_t* currentStart = pos;
current = pos;
pos += colors;
pos += oneChunk;

if(pos == epos || memcmp(pos, current, colors) == 0)
if(pos == epos || memcmp(pos, current, oneChunk) == 0)
{
int8_t repeat = 0;
// Find number of repititions
while(pos != epos && memcmp(pos, current, colors) == 0)
while(pos != epos && memcmp(pos, current, oneChunk) == 0)
{
pos += colors;
pos += oneChunk;
repeat++;
if(repeat == 127)
{
break;
}
}
outBts << repeat;
outBts.putBytes(current, colors);
outBts.putBytes(current, oneChunk);
}
else
{
Expand All @@ -127,14 +141,14 @@ void compress_line(uint8_t* raw, size_t len, Bytestream& outBts, int colors)
do
{
current = pos;
pos += colors;
pos += oneChunk;
verbatim++;
if(verbatim == 127)
{
break;
}
}
while(pos != epos && memcmp(pos, current, colors) != 0);
while(pos != epos && memcmp(pos, current, oneChunk) != 0);

// This and the next sequence are equal,
// assume it starts a repeating sequence.
Expand All @@ -148,15 +162,15 @@ void compress_line(uint8_t* raw, size_t len, Bytestream& outBts, int colors)
// But in order to not lean on that (uint8_t)(256)==0, we have this.
if(verbatim == 1)
{ // We ended up with one sequence, encode it as such
pos = currentStart + colors;
pos = currentStart + oneChunk;
outBts << (uint8_t)0;
outBts.putBytes(currentStart, colors);
outBts.putBytes(currentStart, oneChunk);
}
else
{ // 2 or more non-repeating sequnces
pos = currentStart + verbatim*colors;
outBts << (uint8_t)(257-verbatim);
outBts.putBytes(currentStart, verbatim*colors);
pos = currentStart + verbatim * oneChunk;
outBts << (uint8_t)(257 - verbatim);
outBts.putBytes(currentStart, verbatim * oneChunk);
}
}
}
Expand Down Expand Up @@ -193,7 +207,9 @@ void make_pwg_hdr(Bytestream& outBts, const PrintParameters& params, bool backsi
{PrintParameters::Gray8, PwgPgHdr::sGray},
{PrintParameters::Black8, PwgPgHdr::Black},
{PrintParameters::Gray1, PwgPgHdr::sGray},
{PrintParameters::Black1, PwgPgHdr::Black}};
{PrintParameters::Black1, PwgPgHdr::Black},
{PrintParameters::sRGB48, PwgPgHdr::sRGB},
{PrintParameters::Gray16, PwgPgHdr::sGray}};

outHdr.MediaType = params.mediaType;
outHdr.Duplex = params.isTwoSided();
Expand Down Expand Up @@ -235,14 +251,16 @@ void make_urf_hdr(Bytestream& outBts, const PrintParameters& params)
static const std::map<PrintParameters::ColorMode, UrfPgHdr::ColorSpace_enum>
urfColorSpaceMappings {{PrintParameters::sRGB24, UrfPgHdr::sRGB},
{PrintParameters::CMYK32, UrfPgHdr::CMYK},
{PrintParameters::Gray8, UrfPgHdr::sGray}};
{PrintParameters::Gray8, UrfPgHdr::sGray},
{PrintParameters::sRGB48, UrfPgHdr::sRGB},
{PrintParameters::Gray16, UrfPgHdr::sGray}};

static const std::map<PrintParameters::DuplexMode, UrfPgHdr::Duplex_enum>
urfDuplexMappings {{PrintParameters::OneSided, UrfPgHdr::OneSided},
{PrintParameters::TwoSidedLongEdge, UrfPgHdr::TwoSidedLongEdge},
{PrintParameters::TwoSidedShortEdge, UrfPgHdr::TwoSidedShortEdge}};

outHdr.BitsPerPixel = 8*params.getNumberOfColors();
outHdr.BitsPerPixel = params.getNumberOfColors() * params.getBitsPerColor();
outHdr.ColorSpace = urfColorSpaceMappings.at(params.colorMode);
outHdr.Duplex = urfDuplexMappings.at(params.duplexMode);
outHdr.setQuality(params.quality);
Expand Down
12 changes: 1 addition & 11 deletions lib/ppm2pwg.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,7 @@ Bytestream make_pwg_file_hdr();

Bytestream make_urf_file_hdr(uint32_t pages);

void make_pwg_hdr(Bytestream& outBts, const PrintParameters& params, bool backside);
void make_urf_hdr(Bytestream& outBts, const PrintParameters& params);


void bmp_to_pwg(Bytestream& bmpBts, Bytestream& outBts, size_t page,
const PrintParameters& params);

void compress_lines(Bytestream& bmpBts, Bytestream& outBts,
const PrintParameters& params, bool backside);

void compress_line(uint8_t* raw, size_t len, Bytestream& outBts, int Colors);
void bmp_to_pwg(Bytestream& bmpBts, Bytestream& outBts, size_t page, const PrintParameters& params);

bool isUrfMediaType(std::string mediaType);

Expand Down
17 changes: 14 additions & 3 deletions lib/printparameters.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,14 +110,18 @@ double PrintParameters::getPaperSizeHInPoints() const

size_t PrintParameters::getPaperSizeWInBytes() const
{
if(getBitsPerColor() == 1)
if(getBitsPerColor() == 1 && getNumberOfColors() == 1)
{
// Round up to whole bytes
return getNumberOfColors() * ((getPaperSizeWInPixels() + 7) / 8);
return (getPaperSizeWInPixels() + 7) / 8;
}
else if(getBitsPerColor() % 8 == 0)
{
return getPaperSizeWInPixels() * getNumberOfColors() * (getBitsPerColor() / 8);
}
else
{
return getNumberOfColors() * (getPaperSizeWInPixels() / (8 / getBitsPerColor()));
throw(std::logic_error("Unhandled color and bit depth combination"));
}
}

Expand Down Expand Up @@ -272,6 +276,8 @@ bool PrintParameters::isBlack() const
case CMYK32:
case Gray8:
case Gray1:
case sRGB48:
case Gray16:
return false;
case Black8:
case Black1:
Expand All @@ -286,13 +292,15 @@ size_t PrintParameters::getNumberOfColors() const
switch(colorMode)
{
case sRGB24:
case sRGB48:
return 3;
case CMYK32:
return 4;
case Gray8:
case Black8:
case Gray1:
case Black1:
case Gray16:
return 1;
default:
throw(std::logic_error("Unknown color mode"));
Expand All @@ -311,6 +319,9 @@ size_t PrintParameters::getBitsPerColor() const
case Gray1:
case Black1:
return 1;
case sRGB48:
case Gray16:
return 16;
default:
throw(std::logic_error("Unknown color mode"));
}
Expand Down
4 changes: 3 additions & 1 deletion lib/printparameters.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ class PrintParameters
Gray8,
Black8,
Gray1,
Black1
Black1,
sRGB48,
Gray16
};

enum Quality
Expand Down
2 changes: 1 addition & 1 deletion tests/Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
LDFLAGS = -ldl -Wl,--export-dynamic -lcurl -ljpeg $(shell pkg-config --libs poppler-glib) -lz
CXXFLAGS = -std=c++17 -g -pedantic -Wall -Werror -Wextra -I ../lib -I ../json11 -I ../bytestream \
$(shell pkg-config --cflags poppler-glib)
TEST_INCLUDES = -I ../lib -I ../bytestream/minitest -I ../bytestream/minitest/tests
TEST_INCLUDES = -I ../lib -I ../bytestream/minitest

VPATH = ../bytestream ../lib ../json11

Expand Down
Loading

0 comments on commit 28fbb9a

Please sign in to comment.