Tehnični članak

Paleta BIFF8 s 56 barvami: preslikava OKLab v HotXLS

HotXLS poljubne barve RGB in barve teme preslika na barvno paleto BIFF8 s 56 režami v dveh plasteh: NearestIndexedColor v prostoru OKLab poišče zaznavno najbližji obstoječi vnos palete, BuildBiffPalettePlan in ApplyBiffPalettePlan pa prepišeta proste reže palete, tako da true-color delovni zvezek preživi shranjevanje v klasični XLS. Sprožilec je vedno ista prijava podpore. Nekdo sestavi poročilo v XLSX s podjetniško mornarsko modro glavo in mehkim turkiznim poudarkom, shrani ga kot .xls za starejšega potrošnika, glava pa se vrne čista črna, turkizna pa se spremeni v kričeče turkizno. Nič se ni zrušilo in nobeno opozorilo ni utripnilo. Barvni model starega formata preprosto ne more nositi tega, kar je opisal novi, knjižnica pa si je morala izbrati nekaj

Zakaj lahko datoteka XLS hrani le 56 barv?

Ker format celice BIFF8 nikoli ne shrani vrednosti RGB: pisave, polnila in obrobe nosijo barvni indeks, zapis Palette, skupen vsemu delovnemu zvezku ($0092, [MS-XLS] §2.4.188), pa dobavi natanko 56 motnih vnosov RGB za indekse 8 do 63. Indeksi 0 do 7 so fiksne kopije osmih osnovnih barv, vrednosti nad 63 pa sploh niso barve, ampak žetoni, kot so sistemska barva ospredja, sistemska barva ozadja in besedilo grafikona. HotXLS paleto izpostavi prek javnega ColorIndex od 1 do 56, kar je fizični indeks minus 7, ResolveIndexedColor pa tri sheme številčenja loči prek TXLSIndexedColorSpace: xicsPublicColorIndex za vrednosti API-ja 1..56, xicsBiffIcv za surove indekse z diska, ki so preverjeni proti podmnožici IcvFont, IcvXF ali IcvChart za vlogo, ki jo podate, in xicsOoxmlIndexed, kjer 64 in 65 pomenita sistemsko ospredje in ozadje

HotXLS tri sheme indeksiranih barv loči prek TXLSIndexedColorSpace: surove vrednosti BIFF icv, 0 do 7 fiksirano na osem osnovnih barv, 56 rež palete 8 do 63 zapisa Palette $0092, žetoni nad 63, kot je sistemsko ospredje, javni ColorIndex 1 do 56 z odmikom minus 7 ter xicsOoxmlIndexed, kjer 64 in 65 pomenita sistemsko ospredje in ozadje
Isti barvni indeks pomeni v vsaki shemi drugačna števila, zato HotXLS vsako vrednost usmeri skozi ResolveIndexedColor, namesto da bi surov žeton BIFF nastopal kot javni ColorIndex
var
  Res: TXLSIndexedColorResolution;
begin
  // $40 je žeton BIFF icv, ne reža palete
  Workbook.ResolveIndexedColor($40, xicsBiffIcv, Res);
  case Res.Kind of
    xickPalette:   UseArgb(Res.ARGB);   // reža palete, če je razrešena
    xickAutomatic,
    xickSystem:    UseSystemColor(Res.SystemColorRole);
    xickInvalid:   RejectToken(Res.RawIndex);
  end;
end;

Upoštevajte, da primer preklaplja na Res.Kind in prezre logično povratno vrednost. ResolveIndexedColor vrne True le, kadar je dobil konkreten ARGB, kratka preobremenitev pa nikoli ne bere namizja Windows, zato žeton automatic ali system upravičeno vrne False, čeprav je še vedno razvrščen kot xickSystem. HotXLS je to naletel v svojem lastnem serializatorju delovnega zvezka: koda, ki False obravnava kot »brez barve«, tiho vrže proč pomen Automatic in System žetona. Če potrebujete prave vrednosti RGB za te žetone, pokličite dolgo preobremenitev in podajte povratni klic TXLSTryResolveSystemColor, ki uveljavi Vašo politiko za UI, izvoz ali brezglavi način

Zakaj HotXLS barve ujema v OKLab in ne v RGB?

Ker so vrednosti kanalov sRGB gamma-kodirane, evklidska razdalja v RGB ne sledi temu, kar vidi človek, napaka pa je najhujša prav v temnih, nasičenih tonih, ki jih podjetniške palete obožujejo. Vzemite temno modro $000033. V RGB je razdalja do črne 51, razdalja do privzetega vnosa mornarsko modre $000080 pa 77, zato ujemalec v RGB samozavestno pobarva Vašo glavo črno. V OKLab sta kvadrirani razdalji približno 0,0312 do črne in 0,0235 do mornarsko modre, HotXLS pa izbere mornarsko modro, ColorIndex 11 na fizični reži 18; natanko ta primer je pripet v testnem naboru za oba pogona, Classic in XLSX. Pretvorba v ArgbToOklab linearizira vsak kanal sRGB, uporabi matriko LMS za OKLab, vzame kocke korenine in projektira na L, a in b, po čemer je običajna kvadrirana evklidska razdalja razumen nadomestek za zaznano razliko. OKLab ni CIEDE2000 in se ne pretvarja, da je, nima pa krpanih popravkov odtenka, stane peščico množenj na barvo in je dovolj stabilen, da poganja zanko za razvrščanje v gruče — tam pravzaprav tudi zasluži svoje mesto

Kako HotXLS temno modro $000033 ujema s paleto: evklidska razdalja v gamma-kodiranem RGB meri 51 do črne in 77 do mornarsko modre ter bi glavo pobarvala črno, kvadrirani razdalji 0,0312 in 0,0235 v ArgbToOklab pa pustita NearestIndexedColor, da izbere mornarsko modro, ColorIndex 11 na fizični reži 18
Gamma-kodirane vrednosti kanalov naredijo razdaljo RGB slabim nadomestkom za tisto, kar vidi človek, zato HotXLS enkrat pretvori v OKLab in preiskavo palete prepusti običajni kvadrirani evklidski primerjavi

Kaj zagotavlja NearestIndexedColor?

NearestIndexedColor zagotavlja determinističen odgovor, ki samo bere: ena pretvorba vhoda, ena fiksna preiskava čez 56 predpomnjenih vnosov in najnižji javni indeks, kadar koli sta dva vnosa enako blizu. Vsak delovni zvezek predpomni normalizirani ARGB in koordinate OKLab vseh 56 fizičnih rež skupaj s števcem generacije palete. Ponastavitev palete znova zgradi predpomnilnik, sprememba posamezne reže posodobi le to režo, poizvedba proti zastareli generaciji pa vrne False, namesto da bi ugibala. Preiskava uporablja strogo primerjavo manjše, ki se začne pri reži 8, zato paleta, ki isto barvo vsebuje dvakrat, vedno odgovori z nižjim indeksom; to šteje, ko primerjate dve ustvarjeni datoteki in pričakujete izhod, enak po bajtih. Vhodna alfa sledi ozki pogodbi: ničelni bajt alfe se šteje kot moten, delno prosojna vrednost pa je zavrnjena s ColorIndex 0 in PaletteSlot -1, saj vnosi palete nimajo alfe. Pisci polnil in obrob pogona Classic pretvorijo barve RGB in teme v indeks z isto rutino ujemanja OKLab ob shranjevanju, tako da se API in shranjena datoteka strinjata, v katero režo barva pristane

var
  Match: TXLSNearestIndexedColorMatch;
begin
  if Workbook.NearestIndexedColor($FF000033, Match) then
  begin
    // Match.ColorIndex = 11, Match.PaletteSlot = 18, Match.ARGB = $FF000080
    if not Match.ExactMatch then
      LogApproximation(Match.InputARGB, Match.ARGB, Match.DistanceSquared);
  end;
end;

Kako BuildBiffPalettePlan spravi prave barve v 56 rež?

BuildBiffPalettePlan izračuna popoln predlog za vseh 56 rež, ne da bi se dotaknil delovnega zvezka, zato ga lahko pregledate, zabeležite ali zavržete. Načrtovalnik najprej pokliče ScanIndexedColorUsage: vsaka reža, na katero pisava, polnilo, obroba, pogojni format, oblika, opomba ali mreža delovnega lista sklicuje po indeksu, je zaklenjena, ker sprememba vnosa palete prebarva vse potrošnike tega indeksa na enkrat. Cilji so neposredne barve RGB in razrešene barve teme iz pisav, polnil, obrob, diferencialnih slogov, podatkovnih vrstic in barvnih lestvic. Vsak cilj je utežen z večjim od števila upodobljenih sklicev in števila definicij, pogojni format pa šteje celice, ki jih pokrivajo njegovi obsegi, zato barva, razmazana čez cel stolpec, prevaga nad eno, uporabljeno v posamezni opombi. Namestitev nato poteka v fiksnem vrstnem redu:

  • Zaklenjene reže brezpogojno obdržijo svojo izvorno barvo
  • Cilj, ki že obstaja v paleti, ostane na svoji najnižji ujemajoči se reži, ta reža pa postane fiksna
  • Če se preostali edinstveni cilji prilegajo v proste reže, vsak dobi točno določeno režo, dodeljeno v naraščajočem vrstnem redu ARGB
  • Sicer se nastavi Quantized, vsaka prosta reža je zaseta s ciljem, katerega razdalja do najbližjega obstoječega središča, pomnožena z njegovo utežjo, je največja, do 16 krogov k-means, uteženih po pogostosti, v OKLab pa premika le prosta središča, dokler se dodelitve ne ustalijo

Bodite iskreni do sebe glede tega, kaj dostavi pot za prekoračitev. Razvrščanje v gruče je omejena lokalna optimizacija in ne globalni optimum, prosta reža pa na koncu nosi centroid, pretvorjen nazaj v sRGB s stiskanjem v meje, kar je lahko barva, ki je nobena celica ni uporabila dobesedno. To, kar dobite, je ponovljivost: isti delovni zvezek vedno da isti načrt, načrt pa svojo škodo sporoči prek WeightedError, MaxDistanceSquared, ExactTargetWeight in TotalTargetWeight, zato lahko paketno opravilo zavrne shranjevanje, ko se približek izkaže za preveč grob za smernico blagovne znamke

Cevovod palete HotXLS za true-color delovni zvezek: ScanIndexedColorUsage zaklene vsako režo, na katero sklicuje pisava, polnilo, obroba, pogojni format, oblika, opomba ali mreža, BuildBiffPalettePlan namesti točne barve v naraščajočem vrstnem redu ARGB ali požene do 16 krogov k-means, uteženih po pogostosti, v OKLab, ApplyBiffPalettePlan pa pred zapisom preveri generacijo in hash FNV-1a
Načrtovanje samo bere in je ponovljivo, načrt svojo škodo sporoči prek WeightedError in MaxDistanceSquared, zastarel načrt pa je zavrnjen s paledo nedotaknjeno, ker so načrti v učinku enkratne uporabe
var
  Plan: TXLSBiffPalettePlan;
  I: Integer;
begin
  Plan := Workbook.BuildBiffPalettePlan;   // samo za branje
  if Plan.Quantized and (Plan.MaxDistanceSquared > MaxAcceptedError) then
    raise Exception.Create('Too many distinct colors for a BIFF8 palette');
  for I := 0 to High(Plan.Slots) do
    if Plan.Slots[I].Changed then
      LogSlot(Plan.Slots[I].ColorIndex, Plan.Slots[I].SourceARGB,
        Plan.Slots[I].TargetARGB);
  if not Workbook.ApplyBiffPalettePlan(Plan) then
    raise Exception.Create('The palette changed after planning');
end;

Kako ApplyBiffPalettePlan zavrne zastarel načrt?

ApplyBiffPalettePlan potrdi celoten načrt, še preden zapiše eno samo režo, in vrne False s paleto nedotaknjeno, če se karkoli ne strinja s trenutnim delovnim zvezkom. Načrt nosi SourcePaletteGeneration in SourcePaletteHash, 64-bitni hash FNV-1a nad 56 izvornimi barvami; potrditev še enkrat preveri vsak javni in fizični indeks, vsako izvorno barvo, da nobena zaklenjena reža ni označena kot spremenjena, števca zaklenjenih in spremenjenih ter da je vsak cilj moten. Vsaka dejanska sprememba palete v vmesnem času, vključno z uspešno prejšnjo uporabo istega načrta, naredi načrt zastarel, zato so načrti v učinku enkratne uporabe. Veljaven načrt brez spremenjenih rež uspe, ne da bi premaknil generacijo, prava sprememba pa generacijo dvigne enkrat in enkrat znova zgradi ujemalec OKLab — pri pogonu Classic s prepisovanjem fiksne tabele palete, pri pogonu XLSX pa z zamenjavo pripravljenega seznama preglasitev indeksiranih barv

Vklop za shranjevanja BIFF8 in pretvorbo XLSX v XLS

Lastnost BiffPaletteSavePolicy ima privzeto xbpsPreserve, zato nadgradnja HotXLS nikomur ne prepše palete za hrbtom. Nastavitev na xbpsOptimizeTrueColors naredi, da klasični delovni zvezek zgradi in uveljavi svež načrt znotraj SaveAs, a le, kadar je ciljni format xlExcel97; pisci BIFF5, CSV, HTML, PDF, XLSX in ostali nastavitev ignorirajo. Po uspešnem shranjevanju optimizirana paleta ostane v modelu delovnega zvezka, zato poznejše poizvedbe in shranjevanja vidijo isto preslikavo. Če shranjevanje spodleti ali je preklicano, so obnovljene izvirne 56 barve in izvirna generacija. Za vire XLSX SaveXLSXWorkbookAsXLS v lxXlsxExport zgradi en načrt iz naloženega delovnega zvezka in ga zapiše v ciljno paleto, še preden je pretvorjen kateri koli slog — to je determinističen most, ki ga vadi predstavitveni primer delovne mize za revizijo in pretvorbo delovnih zvezkov. Barve teme gredo skozi isti načrtovalnik, potem ko je njihov odtenek razrešen v RGB; če raje želite teme obdržati žive v polnilih grafikonov, članek o barvah teme GelFrame v polnilih grafikonov razloži, kako binarni XLS shrani indeks sheme namesto sploščene barve

// Klasični delovni zvezek: izrecna vključitev, le BIFF8
Workbook.BiffPaletteSavePolicy := xbpsOptimizeTrueColors;
if Workbook.SaveAs('report.xls', xlExcel97) <> 1 then
  HandleSaveFailure;   // paleta je že obnovljena

// model XLSX v BIFF8 z enim determinističnim načrtom palete
XWorkbook := TXLSXWorkbook.Create;
try
  if XWorkbook.Open('report.xlsx') = 1 then
    SaveXLSXWorkbookAsXLS(XWorkbook, 'report.xls');
finally
  XWorkbook.Free;
end;

API-ji palete HotXLS delujejo enako na IXLSWorkbook in TXLSXWorkbook, iz Delphija in C++Builderja povsem enako. Preskusno različico prenesite in jo usmerite na Vašo najbolj barvito preglednico prek strani komponente HotXLS Delphi Excel