HotPDF stelt drie image downsampling-kernels beschikbaar via de property ImageDownsampleKernel en een aparte Floyd-Steinberg-pas via RenderOutputDither. De eerste bepaalt hoe foto's eruitzien nadat u ze verkleint om binnen een size budget te blijven, de tweede bepaalt hoe ze eruitzien nadat een pagina tot zwart-wit is gereduceerd. Geen van beide staat standaard aan, en beide zijn opt-in om dezelfde reden: ze kosten echte tijd
De druk die mensen hierheen brengt, is bekend. Een gescand contract van 60 MB moet de deur uit via een e-mailgateway die alles boven 10 MB weigert, of een batch afschriften moet belanden op een faxachtig monochroom apparaat dat elke grijze pixel rendert als papier of toner. Beide problemen zijn resamplingproblemen, en beide hebben een snel antwoord dat er slecht uitziet en een traag antwoord dat er goed uitziet
Waar de drie kernels werkelijk in verschillen
THPDFResampleKernel heeft drie waarden, en ze liggen op werkelijk verschillende punten van de snelheid-kwaliteit-curve. rkHalftone delegeert aan het historische GDI StretchBlt-pad met de HALFTONE-modus, dat ondanks de naam bilinear-klasse filtering is: snel, toereikend voor lijntekeningen en screenshots, en gevoelig voor de korrelige randen die u op verkleinde foto's meteen herkent. rkBicubic draait een separabele Catmull-Rom-kernel, en rkLanczos3 draait een separabele windowed sinc met three-lobe support
Beide separabele kernels draaien als twee passen, eerst horizontaal dan verticaal, met 6 tot 12 taps per doelpixel in puur Pascal. Dat is grofweg een orde van grootte trager dan het GDI-pad, en dat is precies waarom rkHalftone de default blijft. Bij een nachtelijke batch van duizenden pagina's is het verschil een planningsbeslissing, geen voorkeur. Bij één document waar een gebruiker op wacht, is Lanczos3 vrijwel gratis en zichtbaar beter
Twee implementatie-eigenschappen zijn het weten waard omdat ze bepalen wat de output wel en niet kan. Randen worden afgeklemd door edge replication in plaats van wrapping of uitfaden, en de gewichten worden per doelpixel genormaliseerd. Samen betekenen die twee dat het resultaat nooit onder zwart of boven wit ringt, dus de klassieke Lanczos-overshoot-halo rond een hardlopende rand verschijnt niet als afgeknipte artifacts in de gecodeerde afbeelding
var
Pdf: THotPDF;
Info: THPDFLoadedResourceOptimizationInfo;
Changed: Integer;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.LoadFromFile('scanned-contract.pdf');
Pdf.ImageDownsampleKernel := rkLanczos3; // vóór de aanroep zetten
Changed := Pdf.DownsampleLoadedImages(150, 82, 4096, Info);
if Changed > 0 then
begin
Writeln('resampled images: ', Info.DownsampledImageCount);
Writeln('kept calibrated : ', Info.PreservedCalibratedImageCount);
Pdf.SaveToFile('scanned-contract-150dpi.pdf');
end;
finally
Pdf.Free;
end;
end;
Het argument MinimumSavingsBytes, hierboven 4096, is de guard die de operatie eerlijk houdt. Een afbeelding die al efficiënt gecomprimeerd was opnieuw encoderen kan een grotere stream opleveren dan het origineel, en een downsampler die blindelings elke afbeelding vervangt, vergroot af en toe de file die hij moest verkleinen. De drempel zegt: commit de vervanging alleen als hij ten minste zoveel bytes bespaart. PreservedCalibratedImageCount rapporteert de andere conservatieve beslissing: afbeeldingen die onaangeroerd blijven omdat ze een gekalibreerde color space dragen die resampling zou aantasten
Waarom is een verkeerde polynomiale coëfficiënt zo moeilijk te spotten?
Omdat een kapotte interpolatiekernel niet crasht of een exceptie gooit, hij produceert gewoon een afbeelding die subtiel verkeerd oogt op een manier die niemand kan herleiden. De Catmull-Rom-kernel is piecewise kubiek, en zijn buitenste tak in geneste Horner-vorm is ((-0.5t + 2.5)t - 4)t + 2. Schrijf die middelste coëfficiënt als -5 in plaats van -4, en de functie evalueert nog steeds, geeft nog steeds getallen in een plausibel bereik en produceert nog steeds een afbeelding
De schade toont zich doordat W(1) evalueert tot -1 waar het 0 moet zijn. Negatieve gewichten stapelen op, de som klemt op nul, en het zichtbare symptoom is een gradient waarvan het linkeruiteinde zwart wordt en een sprongrand die zijn tussentinten verliest. Niets in de failure wijst naar een polynoom. De check die hem in seconden vangt, is rekenkundig in plaats van visueel: een interpolerende kernel moet voldoen aan W(0) = 1 en W(±1) = W(±2) = 0, en elke kernel die die drie punten mist, heeft een coëfficiëntfout, punt. Assert in een unit test op die drie waarden en de hele categorie typefout-defecten verdwijnt
Floyd-Steinberg dithering, en waar die in de pipeline thuishoort
De dither-pas is een ander probleem dan resampling en huist op een ander punt in de pipeline. RenderOutputDither past Floyd-Steinberg error diffusion toe na page composition, en dat is de enige plek die zin heeft voor een monochrome print preview of een faxachtige export: de operatie gaat over het reduceren van een afgewerkt raster naar één bit per pixel, niet over hoe individuele afbeeldingen onderweg geschaald werden
Het algoritme zelf is kort. De luminantie krijgt een drempel van 50 procent, en de kwantisatiefout wordt gediffundeerd naar vier buren met de klassieke gewichten 7/16, 3/16, 5/16 en 1/16, richting rechts, linksonder, onder en rechtsonder. De outputpixel is 0 of 255 in elk kanaal. Wat het naïeve alternatief u in plaats daarvan geeft, een harde drempel zonder diffusie, maakt van een foto een silhouet en verliest elke midtone die de inhoud droeg
// Dithering op rendertijd voor een monochroom preview-apparaat
Pdf.RenderOutputDither := True;
// Of pas dezelfde toe op een bitmap die u al heeft. De bitmap moet
// pf24bit zijn; de functie geeft False terug in plaats van te gokken
if not HPDFFloydSteinbergDitherBitmap(Preview) then
raise Exception.Create('dither expects a 24-bit bitmap');
// Directe kerneltoegang als u buiten de documentpipeline resampled
Small := HPDFResizeBitmapKernel(Large, 640, 480, rkLanczos3);
try
Small.SaveToFile('thumb.bmp');
finally
Small.Free;
end;
Er is één implementatiedetail in error diffusion dat iedereen een keer bijt. De foutbuffer van rij naar rij moet accumuleren. Elke pixel in de volgende rij ontvangt bijdragen van drie verschillende pixels in de huidige rij, de 3/16-, 5/16- en 1/16-taps, en als de code toewijst in plaats van optelt, gooit elke schrijfactie de vorige bijdrage weg en overleeft alleen de laatste tap. De afbeelding ziet er nog steeds geditherd uit, en dat is wat het moeilijk te zien maakt, maar de textuur is verkeerd en de tonale reproductie drijft af. De test die het vangt, is kwantitatief: dither een egalig middengrijs veld en eis dat de binnendekking tussen 40 en 60 procent uitkomt
Welke combinatie hoort in een size-reductie-pipeline?
Koppel de kernel aan wat de afbeeldingen werkelijk zijn, en behandel dithering als een apparaatkwestie in plaats van een compressiekwestie. Voor fotografische scans die een size budget moeten overleven, houdt rkLanczos3 op 150 of 200 DPI het detail vast dat mensen opmerken terwijl het pixeraantal met een factor vier of meer keldert. Voor screenshots, diagrammen en lijntekeningen is rkHalftone echt prima en veel sneller, want die afbeeldingen hebben weinig tonale gradients te behouden. Voor een gemengde batch waarin u niet elke afbeelding kunt inspecteren, is rkBicubic het redelijke midden: beter dan bilinear, ruwweg de helft van het tapaantal van Lanczos3
Downsampling is één van de hendels, en niet altijd de grootste. Bilevel-scans reageren doorgaans veel beter op de encoder uit native JBIG2 bilevel compressie in Delphi, waar de winst uit symbol dictionaries komt in plaats van uit pixeraantallen. Voordat u besluit, helpt het te weten wat er werkelijk in de file zit, en daarvoor is afbeeldingen en hun decode filters extraheren bedoeld: een inventaris van image-objecten en hun bestaande compressie vertelt u of resampling iets te halen heeft
Bouwt u het preview-oppervlak dat het resultaat toont, dan is het renderpad gedocumenteerd in een PDF-pagina naar een bitmap renderen de plek waar RenderOutputDither doorwerkt, zodat de geditherde preview en de geditherde output uit één coderoute komen in plaats van uit twee implementaties die uit elkaar drijven
Het brede principe achter beide features is dat kwaliteitsinstellingen expliciet en omkeerbaar moeten zijn. HotPDF laat het historische gedrag de default blijven, zodat een bestaande applicatie upgradet zonder verrassende verandering in output of timing, en zet de mooiere, tragere paden één property-toewijzing verderop. Beide maken deel uit van de HotPDF Delphi PDF component, naast de resource-optimalisatie en rendering-machinery waarop ze voortbouwen