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