Artikel Teknis

Palet 56 Warna BIFF8: Pemetaan Warna OKLab di HotXLS

HotXLS memetakan RGB dan warna tema apa pun ke palet warna BIFF8 56 slot dalam dua lapisan: NearestIndexedColor mencari entri palet yang sudah ada yang paling dekat secara persepsi di ruang OKLab, sementara BuildBiffPalettePlan dan ApplyBiffPalettePlan menulis ulang slot palet yang bebas agar workbook true-color selamat melewati simpanan ke XLS klasik. Pemicunya selalu tiket dukungan yang sama. Seseorang membuat laporan di XLSX dengan header biru navy korporat dan aksen teal lembut, menyimpannya sebagai .xls untuk aplikasi lawas, lalu header keluar hitam pekat sementara teal berubah jadi turquoise yang mencolok. Tidak ada yang crash dan tidak ada peringatan yang muncul. Model warna format lama memang tak mampu menampung apa yang dideskripsikan format baru, dan library terpaksa memilih sesuatu

Mengapa berkas XLS hanya bisa menampung 56 warna?

Karena format sel BIFF8 tidak pernah menyimpan nilai RGB: font, fill, dan border membawa indeks warna, dan record Palette di tingkat workbook ($0092, [MS-XLS] §2.4.188) menyediakan tepat 56 entri RGB opaque untuk indeks 8 sampai 63. Indeks 0 sampai 7 adalah salinan tetap dari delapan warna dasar, dan nilai di atas 63 bukan warna sama sekali melainkan token seperti system foreground, system background, dan chart text. HotXLS mengekspos palet lewat ColorIndex publik 1 sampai 56, yaitu indeks fisik dikurangi 7, dan ResolveIndexedColor memisahkan tiga skema penomoran itu lewat TXLSIndexedColorSpace: xicsPublicColorIndex untuk nilai API 1..56, xicsBiffIcv untuk indeks mentah di disk yang divalidasi terhadap subset IcvFont, IcvXF, atau IcvChart sesuai peran yang Anda berikan, dan xicsOoxmlIndexed yang di dalamnya 64 dan 65 berarti system foreground dan background

HotXLS memisahkan tiga skema warna terindeks lewat TXLSIndexedColorSpace: nilai icv BIFF mentah, 0 sampai 7 tetap sebagai delapan warna dasar, 56 slot palet 8 sampai 63 milik record Palette $0092, token di atas 63 seperti system foreground, ColorIndex publik 1 sampai 56 dengan offset minus 7, dan xicsOoxmlIndexed yang 64 dan 65-nya berarti system foreground dan background
Indeks warna yang sama berarti angka berbeda di tiap skema, jadi HotXLS melewatkan setiap nilai melalui ResolveIndexedColor alih-alih membiarkan token BIFF mentah menyaru sebagai ColorIndex publik
var
  Res: TXLSIndexedColorResolution;
begin
  // $40 adalah token icv BIFF, bukan slot palet
  Workbook.ResolveIndexedColor($40, xicsBiffIcv, Res);
  case Res.Kind of
    xickPalette:   UseArgb(Res.ARGB);   // slot palet, jika berhasil di-resolve
    xickAutomatic,
    xickSystem:    UseSystemColor(Res.SystemColorRole);
    xickInvalid:   RejectToken(Res.RawIndex);
  end;
end;

Perhatikan bahwa contoh melakukan switch pada Res.Kind dan mengabaikan nilai kembalian Boolean. ResolveIndexedColor hanya mengembalikan True ketika ia memperoleh ARGB konkret, dan overload pendeknya tidak pernah membaca desktop Windows, sehingga token automatic atau system sah-sah saja kembali False meski tetap terklasifikasi sebagai xickSystem. HotXLS menabrak ini di workbook serializer-nya sendiri: kode yang memperlakukan False sebagai "no color" diam-diam membuang makna Automatic dan System dari token tersebut. Kalau Anda butuh nilai RGB nyata untuk token-token itu, panggil overload panjangnya dan sediakan callback TXLSTryResolveSystemColor yang menerapkan kebijakan UI, export, atau headless milik Anda sendiri

Mengapa HotXLS mencocokkan warna di OKLab alih-alih RGB?

Karena nilai channel sRGB adalah gamma-encoded, jarak Euclidean di RGB tidak mengikuti apa yang dilihat manusia, dan errornya paling buruk justru di nada gelap yang pekat itu, favorit palet korporat. Ambil biru gelap $000033. Di RGB jaraknya ke hitam 51 dan jaraknya ke entri navy default $000080 adalah 77, jadi matcher RGB dengan percaya diri mengecat header Anda hitam. Di OKLab jarak kuadratnya sekitar 0.0312 ke hitam dan 0.0235 ke navy, dan HotXLS memilih navy, ColorIndex 11 di slot fisik 18; kasus persis ini dipatok di test suite untuk engine Classic maupun XLSX. Konversi di dalam ArgbToOklab melinearkan tiap channel sRGB, menerapkan matriks LMS OKLab, menarik akar kubik, lalu memproyeksikan ke L, a, dan b, dan setelah itu jarak Euclidean kuadrat biasa sudah jadi proxy yang wajar untuk perbedaan yang dipersepsi. OKLab bukan CIEDE2000 dan tidak mengaku demikian, tapi ia bebas koreksi hue piecewise, hanya menghabiskan segelintir perkalian per warna, dan cukup stabil untuk menggerakkan loop clustering, di sanalah posisinya benar-benar terbukti

Cara HotXLS mencocokkan biru gelap $000033 ke palet: jarak Euclidean di RGB gamma-encoded terukur 51 ke hitam dan 77 ke navy sehingga header akan ternoda hitam, sementara jarak kuadrat ArgbToOklab 0.0312 dan 0.0235 membuat NearestIndexedColor memilih navy, ColorIndex 11 di slot fisik 18
Nilai channel gamma-encoded membuat jarak RGB jadi proxy yang buruk untuk apa yang dilihat manusia, jadi HotXLS mengonversi sekali ke OKLab dan membiarkan perbandingan Euclidean kuadrat biasa mengemudikan pemindaian palet

Apa jaminan yang diberikan NearestIndexedColor?

NearestIndexedColor menjamin jawaban yang deterministik dan read-only: satu konversi input, satu pemindaian tetap atas 56 entri cache, dan indeks publik terendah setiap kali dua entri sama dekatnya. Setiap workbook meng-cache ARGB ternormalisasi dan koordinat OKLab seluruh 56 slot fisik beserta counter generasi palet. Reset palet membangun ulang cache, perubahan satu slot hanya memperbarui slot itu, dan query terhadap generasi basi mengembalikan False alih-alih menebak. Pemindaian memakai perbandingan less-than ketat yang mulai dari slot 8, itulah sebabnya palet yang memuat warna yang sama dua kali selalu menjawab dengan indeks lebih rendah; hal ini penting saat Anda melakukan diff dua berkas hasil generate dan mengharapkan output identik byte demi byte. Alpha input mengikuti kontrak sempit: byte alpha nol diperlakukan opaque, dan nilai semi transparan ditolak dengan ColorIndex 0 dan PaletteSlot -1, karena entri palet tidak punya alpha. Writer fill dan border engine Classic mengonversi RGB dan warna tema ke indeks dengan rutinitas pencocokan OKLab yang sama saat menyimpan, jadi API dan berkas tersimpan sepakat soal slot mana tempat sebuah warna mendarat

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

Bagaimana BuildBiffPalettePlan memasukkan true color ke 56 slot?

BuildBiffPalettePlan menghitung usulan lengkap untuk seluruh 56 slot tanpa menyentuh workbook, jadi Anda bisa memeriksa, mencatat, atau membuangnya. Planner pertama-tama memanggil ScanIndexedColorUsage: slot mana pun yang direferensikan lewat indeks oleh font, fill, border, conditional format, shape, comment, atau gridline worksheet ikut dikunci, karena mengubah satu entri palet sekaligus mewarnai ulang semua konsumen indeks itu. Targetnya adalah warna RGB langsung dan warna tema yang sudah di-resolve dari font, fill, border, differential style, data bar, dan color scale. Tiap target diberi bobot dari yang lebih besar antara jumlah referensi yang dirender dan jumlah definisinya, dan conditional format menghitung sel yang dicakup range-nya, jadi warna yang terbentang di satu kolom penuh mengungguli warna yang dipakai satu catatan saja. Penempatan lalu berjalan dalam urutan tetap:

  • Slot terkunci mempertahankan warna sumbernya tanpa syarat
  • Target yang sudah ada di palet dipertahankan pada slot terendah yang cocok dan slot itu menjadi tetap
  • Bila target unik yang tersisa muat di slot bebas, masing-masing mendapat slot persis, ditugaskan dalam urutan ARGB menaik
  • Bila tidak, Quantized diset, tiap slot bebas di-seed dengan target yang jaraknya ke pusat terdekat yang sudah ada, dikalikan bobotnya, paling besar, dan hingga 16 ronde k-means berbobot frekuensi di OKLab hanya menggeser pusat-pusat bebas sampai penugasan berhenti berubah

Jujurlah kepada diri sendiri soal apa yang dihasilkan jalur overflow ini. Clustering-nya adalah optimasi lokal terbatas, bukan optimum global, dan slot bebas pada akhirnya menampung centroid yang dikonversi balik ke sRGB dengan clamping, yang bisa jadi warna yang tidak pernah dipakai sel mana pun kata demi kata. Yang Anda dapat adalah keterulangan: workbook yang sama selalu menghasilkan plan yang sama, dan plan melaporkan kerusakannya sendiri lewat WeightedError, MaxDistanceSquared, ExactTargetWeight, dan TotalTargetWeight, sehingga batch job bisa menolak menyimpan ketika aproksimasinya terlalu kasar untuk brand guideline

Pipeline palet HotXLS untuk workbook true-color: ScanIndexedColorUsage mengunci tiap slot yang direferensikan font, fill, border, conditional format, shape, comment, atau gridline, BuildBiffPalettePlan menempatkan warna persis dalam urutan ARGB menaik atau menjalankan hingga 16 ronde k-means berbobot frekuensi di OKLab, dan ApplyBiffPalettePlan memvalidasi generasi serta hash FNV-1a sebelum menulis
Perencanaan bersifat read-only dan dapat diulang, plan melaporkan kerusakannya sendiri lewat WeightedError dan MaxDistanceSquared, dan plan basi ditolak dengan palet tak tersentuh karena plan pada dasarnya sekali pakai
var
  Plan: TXLSBiffPalettePlan;
  I: Integer;
begin
  Plan := Workbook.BuildBiffPalettePlan;   // hanya membaca
  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;

Bagaimana ApplyBiffPalettePlan menolak plan yang basi?

ApplyBiffPalettePlan memvalidasi seluruh plan sebelum menulis satu slot pun, dan mengembalikan False dengan palet tak tersentuh bila ada saja yang tidak cocok dengan workbook saat ini. Plan membawa SourcePaletteGeneration dan SourcePaletteHash, hash FNV-1a 64-bit atas 56 warna sumber; validasi juga memeriksa ulang tiap indeks publik dan fisik, tiap warna sumber, memastikan tidak ada slot terkunci yang ditandai berubah, jumlah terkunci dan berubah, serta memastikan semua target opaque. Perubahan palet efektif apa pun di sela waktu, termasuk penerapan sukses plan yang sama sebelumnya, membuat plan itu basi, jadi plan pada praktiknya sekali pakai. Plan valid tanpa slot berubah berhasil tanpa memajukan generasi, dan perubahan nyata menaikkan generasi sekali dan membangun ulang matcher OKLab sekali — di engine Classic dengan menulis ulang array palet tetap, di engine XLSX dengan menukar daftar override warna terindeks yang sudah disiapkan

Menyalakannya untuk simpanan BIFF8 dan konversi XLSX ke XLS

Properti BiffPaletteSavePolicy default-nya xbpsPreserve, jadi meng-upgrade HotXLS tidak pernah menulis ulang palet siapa pun diam-diam. Menyetelnya ke xbpsOptimizeTrueColors membuat workbook Classic menyusun dan menerapkan plan baru di dalam SaveAs, tapi hanya ketika format targetnya xlExcel97; writer BIFF5, CSV, HTML, PDF, XLSX, dan lainnya mengabaikan setelan ini. Setelah simpanan sukses, palet yang teroptimasi tetap tinggal di model workbook, jadi query dan simpanan berikutnya melihat pemetaan yang sama. Bila simpanan gagal atau dibatalkan, 56 warna asli dan generasi asli dipulihkan. Untuk sumber XLSX, SaveXLSXWorkbookAsXLS di lxXlsxExport menyusun satu plan dari workbook yang dimuat dan menulisnya ke palet tujuan sebelum style mana pun dikonversi — inilah jembatan deterministik yang dilatih demo workbook audit and conversion workbench. Warna tema melewati planner yang sama setelah tint-nya di-resolve ke RGB; kalau Anda lebih suka tema tetap hidup di chart fill, artikel GelFrame theme color chart fills membahas cara XLS biner menyimpan indeks skema alih-alih warna yang diratakan

// Workbook Classic: opt in, khusus BIFF8
Workbook.BiffPaletteSavePolicy := xbpsOptimizeTrueColors;
if Workbook.SaveAs('report.xls', xlExcel97) <> 1 then
  HandleSaveFailure;   // palet sudah dipulihkan

// Model XLSX ke BIFF8 dengan satu plan palet deterministik
XWorkbook := TXLSXWorkbook.Create;
try
  if XWorkbook.Open('report.xlsx') = 1 then
    SaveXLSXWorkbookAsXLS(XWorkbook, 'report.xls');
finally
  XWorkbook.Free;
end;

API palet HotXLS bekerja dengan cara yang sama di IXLSWorkbook maupun TXLSXWorkbook, dari Delphi dan C++Builder. Unduh trial-nya dan arahkan ke spreadsheet paling berwarna milik Anda dari halaman komponen Excel Delphi HotXLS