xref: /core/svx/source/tbxctrls/Palette.cxx (revision b42fad8a35e1f16c5a6691dcbc32abc9830d8efa)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <o3tl/string_view.hxx>
21 #include <svx/Palette.hxx>
22 #include <tools/stream.hxx>
23 
24 #include <palettes.hxx>
25 #include <utility>
26 
~Palette()27 Palette::~Palette()
28 {
29 }
30 
~PaletteASE()31 PaletteASE::~PaletteASE()
32 {
33 }
34 
PaletteASE(OUString aFPath,OUString aFName)35 PaletteASE::PaletteASE( OUString aFPath, OUString aFName ) :
36     mbValidPalette( false ),
37     maFPath (std::move( aFPath )),
38     maASEPaletteName  (std::move( aFName ))
39 {
40     LoadPalette();
41 }
42 
LoadColorSet(SvxColorValueSet & rColorSet)43 void PaletteASE::LoadColorSet(SvxColorValueSet& rColorSet)
44 {
45     rColorSet.Clear();
46     int nIx = 1;
47     for (const auto& rColor : maColors)
48     {
49         rColorSet.InsertItem(nIx, rColor.m_aColor, rColor.m_aName);
50         ++nIx;
51     }
52 }
53 
GetName()54 const OUString& PaletteASE::GetName()
55 {
56     return maASEPaletteName;
57 }
58 
GetPath()59 const OUString& PaletteASE::GetPath()
60 {
61     return maFPath;
62 }
63 
IsValid()64 bool PaletteASE::IsValid()
65 {
66     return mbValidPalette;
67 }
68 
69 // CMYK values from 0 to 1
70 // TODO: Deduplicate me (taken from core/cui/source/dialogs/colorpicker.cxx)
lcl_CMYKtoRGB(float fCyan,float fMagenta,float fYellow,float fKey,float & dR,float & dG,float & dB)71 static void lcl_CMYKtoRGB( float fCyan, float fMagenta, float fYellow, float fKey, float& dR, float& dG, float& dB )
72 {
73     fCyan = (fCyan * ( 1.0 - fKey )) + fKey;
74     fMagenta = (fMagenta * ( 1.0 - fKey )) + fKey;
75     fYellow = (fYellow * ( 1.0 - fKey )) + fKey;
76 
77     dR = std::clamp( 1.0 - fCyan, 0.0, 1.0 );
78     dG = std::clamp( 1.0 - fMagenta, 0.0, 1.0 );
79     dB = std::clamp( 1.0 - fYellow, 0.0, 1.0 );
80 }
81 
82 // This function based on code under ALv2 - Copyright 2013 István Ujj-Mészáros
83 // credit Avisek Das and István Ujj-Mészáros
lcl_XYZtoRGB(float fX,float fY,float fZ,float & dR,float & dG,float & dB)84 static void lcl_XYZtoRGB( float fX, float fY, float fZ, float& dR, float& dG, float& dB)
85 {
86     // Observer = 2°, Illuminant = D65
87     fX = fX / 100;
88     fY = fY / 100;
89     fZ = fZ / 100;
90 
91     // X from 0 to 95.047
92     dR = fX * 3.2406 + fY * -1.5372 + fZ * -0.4986;
93     // Y from 0 to 100.000
94     dG = fX * -0.9689 + fY * 1.8758 + fZ * 0.0415;
95     // Z from 0 to 108.883
96     dB = fX * 0.0557 + fY * -0.2040 + fZ * 1.0570;
97 
98     if (dR > 0.0031308)
99     {
100         dR = 1.055 * (std::pow(dR, 0.41666667)) - 0.055;
101     }
102     else
103     {
104         dR = 12.92 * dR;
105     }
106 
107     if (dG > 0.0031308)
108     {
109         dG = 1.055 * (std::pow(dG, 0.41666667)) - 0.055;
110     }
111     else
112     {
113         dG = 12.92 * dG;
114     }
115 
116     if (dB > 0.0031308)
117     {
118         dB = 1.055 * (std::pow(dB, 0.41666667)) - 0.055;
119     }
120     else
121     {
122         dB = 12.92 * dB;
123     }
124     dR *= 255;
125     dG *= 255;
126     dB *= 255;
127 }
128 
129 // This function based on code under ALv2 - Copyright 2013 István Ujj-Mészáros
130 // credit Avisek Das and István Ujj-Mészáros
lcl_LABtoXYZ(float fL,float fa,float fb,float & dX,float & dY,float & dZ)131 static void lcl_LABtoXYZ( float fL, float fa, float fb, float& dX, float& dY, float& dZ)
132 {
133     dY = (fL + 16) / 116;
134     dX = (fa / 500) + dY;
135     dZ = dY - (fb / 200);
136 
137     if (std::pow(dY, 3) > 0.008856)
138     {
139         dY = std::pow(dY, 3);
140     }
141     else
142     {
143         dY = (dY - 0.137931034) /  7.787;
144     }
145 
146     if (std::pow(dX, 3) > 0.008856)
147     {
148         dX = std::pow(dX, 3);
149     }
150     else
151     {
152         dX = (dX - 0.137931034) /  7.787;
153     }
154 
155     if (std::pow(dZ, 3) > 0.008856)
156     {
157         dZ = std::pow(dZ, 3);
158     }
159     else
160     {
161         dZ = (dZ - 0.137931034) /  7.787;
162     }
163 
164     // Observer = 2°, Illuminant = D65
165     dX = 95.047 * dX;
166     dY = 100.000 * dY;
167     dZ = 108.883 * dZ;
168 }
169 
lcl_LABtoRGB(float fL,float fa,float fb,float & dR,float & dG,float & dB)170 static void lcl_LABtoRGB( float fL, float fa, float fb, float& dR, float& dG, float& dB)
171 {
172     float x, y, z;
173     lcl_LABtoXYZ(fL, fa, fb, x, y, z);
174 
175     lcl_XYZtoRGB(x, y, z, dR, dG, dB);
176 }
177 
LoadPalette()178 void PaletteASE::LoadPalette()
179 {
180     SvFileStream aFile(maFPath, StreamMode::READ);
181     aFile.SetEndian(SvStreamEndian::BIG);
182 
183     // Verify magic first 4 characters
184     char cMagic[5] = {0};
185     if ((aFile.ReadBytes(cMagic, 4) != 4) || (strncmp(cMagic, "ASEF", 4) != 0))
186     {
187         mbValidPalette = false;
188         return;
189     }
190 
191     // Ignore the version number
192     aFile.SeekRel(4);
193 
194     sal_uInt32 nBlocks = 0;
195     aFile.ReadUInt32(nBlocks);
196     for (sal_uInt32 nI = 0; nI < nBlocks; nI++) {
197         sal_uInt32 nChunkType = 0;
198         aFile.ReadUInt32(nChunkType);
199         // End chunk
200         if (nChunkType == 0)
201            break;
202 
203         // Grab chunk size, name length
204         sal_uInt16 nChunkSize = 0;
205         sal_uInt16 nChars = 0;
206         aFile.ReadUInt16(nChunkSize);
207         aFile.ReadUInt16(nChars);
208 
209         OUString aPaletteName(u""_ustr);
210         if (nChars > 1)
211             aPaletteName = read_uInt16s_ToOUString(aFile, nChars);
212         else
213             aFile.SeekRel(2);
214 
215         if (nChunkType == 0xC0010000)
216         {
217             // Got a start chunk, so set palette name
218             maASEPaletteName = aPaletteName;
219             // Is there color data? (shouldn't happen in a start block, but check anyway)
220             if (nChunkSize > ((nChars * 2) + 2))
221                 aPaletteName.clear();
222             else
223                 continue;
224         }
225 
226         char cColorModel[5] = {0};
227         aFile.ReadBytes(cColorModel, 4);
228         OString aColorModel(cColorModel);
229         // r, g, and b are floats ranging from 0 to 1
230         float r = 0, g = 0, b = 0;
231 
232         if (aColorModel.equalsIgnoreAsciiCase("cmyk"))
233         {
234             float c = 0, m = 0, y = 0, k = 0;
235             aFile.ReadFloat(c);
236             aFile.ReadFloat(m);
237             aFile.ReadFloat(y);
238             aFile.ReadFloat(k);
239             lcl_CMYKtoRGB(c, m, y, k, r, g, b);
240         }
241         else if (aColorModel.equalsIgnoreAsciiCase("rgb "))
242         {
243             aFile.ReadFloat(r);
244             aFile.ReadFloat(g);
245             aFile.ReadFloat(b);
246         }
247         else if (aColorModel.equalsIgnoreAsciiCase("gray"))
248         {
249             float nVal = 0;
250             aFile.ReadFloat(nVal);
251             r = g = b = nVal;
252         }
253         else if (aColorModel.equalsIgnoreAsciiCase("LAB "))
254         {
255             float fL = 0, fA = 0, fB = 0;
256             aFile.ReadFloat(fL);
257             aFile.ReadFloat(fA);
258             aFile.ReadFloat(fB);
259             lcl_LABtoRGB(fL, fA, fB, r, g, b);
260         }
261 
262         // Ignore color type
263         aFile.SeekRel(2);
264         maColors.emplace_back(Color(r * 255, g * 255, b * 255), aPaletteName);
265     }
266 
267     mbValidPalette = true;
268 }
269 
270 // PaletteGPL ------------------------------------------------------------------
271 
272 static OString lcl_getToken(OStringBuffer& rStr, sal_Int32& index);
273 
PaletteGPL(OUString aFPath,OUString aFName)274 PaletteGPL::PaletteGPL( OUString aFPath, OUString aFName ) :
275     mbLoadedPalette( false ),
276     mbValidPalette( false ),
277     maFName(std::move( aFName )),
278     maFPath(std::move( aFPath ))
279 {
280     LoadPaletteHeader();
281 }
282 
~PaletteGPL()283 PaletteGPL::~PaletteGPL()
284 {
285 }
286 
GetName()287 const OUString& PaletteGPL::GetName()
288 {
289     return maGPLPaletteName;
290 }
291 
GetPath()292 const OUString& PaletteGPL::GetPath()
293 {
294     return maFPath;
295 }
296 
LoadColorSet(SvxColorValueSet & rColorSet)297 void PaletteGPL::LoadColorSet(SvxColorValueSet& rColorSet)
298 {
299     LoadPalette();
300 
301     rColorSet.Clear();
302     int nIx = 1;
303     for (const auto& rColor : maColors)
304     {
305         rColorSet.InsertItem(nIx, rColor.m_aColor, rColor.m_aName);
306         ++nIx;
307     }
308 }
309 
IsValid()310 bool PaletteGPL::IsValid()
311 {
312     return mbValidPalette;
313 }
314 
ReadPaletteHeader(SvFileStream & rFileStream)315 bool PaletteGPL::ReadPaletteHeader(SvFileStream& rFileStream)
316 {
317     OStringBuffer aLine;
318 
319     rFileStream.ReadLine(aLine);
320     if (!std::string_view(aLine).starts_with("GIMP Palette"))
321         return false;
322     rFileStream.ReadLine(aLine);
323     if (std::string_view aPaletteName; o3tl::starts_with(aLine, "Name: ", &aPaletteName))
324     {
325         maGPLPaletteName = OStringToOUString(aPaletteName, RTL_TEXTENCODING_ASCII_US);
326         rFileStream.ReadLine(aLine);
327         if (std::string_view(aLine).starts_with("Columns: "))
328             rFileStream.ReadLine(aLine); // we can ignore this
329     }
330     else
331     {
332         maGPLPaletteName = maFName;
333     }
334     return true;
335 }
336 
LoadPaletteHeader()337 void PaletteGPL::LoadPaletteHeader()
338 {
339     SvFileStream aFile(maFPath, StreamMode::READ);
340     mbValidPalette = ReadPaletteHeader( aFile );
341 }
342 
LoadPalette()343 void PaletteGPL::LoadPalette()
344 {
345     if( mbLoadedPalette ) return;
346     mbLoadedPalette = true;
347 
348     // TODO add error handling!!!
349     SvFileStream aFile(maFPath, StreamMode::READ);
350     mbValidPalette = ReadPaletteHeader( aFile );
351 
352     if( !mbValidPalette ) return;
353 
354     OStringBuffer aLine;
355     do {
356         if (aLine.isEmpty())
357             continue;
358 
359         if (aLine[0] != '#' && aLine[0] != '\n')
360         {
361             // TODO check if r,g,b are 0<= x <=255, or just clamp?
362             sal_Int32 nIndex = 0;
363             OString token;
364 
365             token = lcl_getToken(aLine, nIndex);
366             if(token.isEmpty() || nIndex == -1) continue;
367             sal_Int32 r = token.toInt32();
368 
369             token = lcl_getToken(aLine, nIndex);
370             if(token.isEmpty() || nIndex == -1) continue;
371             sal_Int32 g = token.toInt32();
372 
373             token = lcl_getToken(aLine, nIndex);
374             if(token.isEmpty()) continue;
375             sal_Int32 b = token.toInt32();
376 
377             std::string_view name;
378             if(nIndex != -1)
379                 name = std::string_view(aLine).substr(nIndex);
380 
381             maColors.emplace_back(
382                 Color(r, g, b),
383                 OStringToOUString(name, RTL_TEXTENCODING_ASCII_US));
384         }
385     } while (aFile.ReadLine(aLine));
386 }
387 
388 // finds first token in rStr from index, separated by whitespace
389 // returns position of next token in index
lcl_getToken(OStringBuffer & rStr,sal_Int32 & index)390 static OString lcl_getToken(OStringBuffer& rStr, sal_Int32& index)
391 {
392     sal_Int32 substart, toklen = 0;
393     OUString aWhitespaceChars( u" \n\t"_ustr );
394 
395     while(index < rStr.getLength() &&
396             aWhitespaceChars.indexOf( rStr[index] ) != -1)
397         ++index;
398     if(index == rStr.getLength())
399     {
400         index = -1;
401         return OString();
402     }
403     substart = index;
404 
405     //counts length of token
406     while(index < rStr.getLength() &&
407             aWhitespaceChars.indexOf( rStr[index] ) == -1 )
408     {
409         ++index;
410         ++toklen;
411     }
412 
413     //counts to position of next token
414     while(index < rStr.getLength() &&
415             aWhitespaceChars.indexOf( rStr[index] ) != -1 )
416         ++index;
417     if(index == rStr.getLength())
418         index = -1;
419 
420     return OString(std::string_view(rStr).substr(substart, toklen));
421 }
422 
423 // PaletteSOC ------------------------------------------------------------------
424 
PaletteSOC(OUString aFPath,OUString aFName)425 PaletteSOC::PaletteSOC( OUString aFPath, OUString aFName ) :
426     mbLoadedPalette( false ),
427     maFPath(std::move( aFPath )),
428     maSOCPaletteName(std::move( aFName ))
429 {
430 }
431 
~PaletteSOC()432 PaletteSOC::~PaletteSOC()
433 {
434 }
435 
GetName()436 const OUString& PaletteSOC::GetName()
437 {
438     return maSOCPaletteName;
439 }
440 
GetPath()441 const OUString& PaletteSOC::GetPath()
442 {
443     return maFPath;
444 }
445 
LoadColorSet(SvxColorValueSet & rColorSet)446 void PaletteSOC::LoadColorSet(SvxColorValueSet& rColorSet)
447 {
448     if( !mbLoadedPalette )
449     {
450         mbLoadedPalette = true;
451         mpColorList = XPropertyList::AsColorList(XPropertyList::CreatePropertyListFromURL(XPropertyListType::Color, maFPath));
452         (void)mpColorList->Load();
453     }
454     rColorSet.Clear();
455     if( mpColorList.is() )
456         rColorSet.addEntriesForXColorList( *mpColorList );
457 }
458 
IsValid()459 bool PaletteSOC::IsValid()
460 {
461     return true;
462 }
463 
464 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
465