1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <svx/Palette.hxx>
21 #include <tools/stream.hxx>
23 #include <palettes.hxx>
30 PaletteASE::~PaletteASE()
34 PaletteASE::PaletteASE( OUString aFPath
, OUString aFName
) :
35 mbValidPalette( false ),
36 maFPath (std::move( aFPath
)),
37 maASEPaletteName (std::move( aFName
))
42 void PaletteASE::LoadColorSet(SvxColorValueSet
& rColorSet
)
46 for (const auto& rColor
: maColors
)
48 rColorSet
.InsertItem(nIx
, rColor
.m_aColor
, rColor
.m_aName
);
53 const OUString
& PaletteASE::GetName()
55 return maASEPaletteName
;
58 const OUString
& PaletteASE::GetPath()
63 bool PaletteASE::IsValid()
65 return mbValidPalette
;
68 // CMYK values from 0 to 1
69 // TODO: Deduplicate me (taken from core/cui/source/dialogs/colorpicker.cxx)
70 static void lcl_CMYKtoRGB( float fCyan
, float fMagenta
, float fYellow
, float fKey
, float& dR
, float& dG
, float& dB
)
72 fCyan
= (fCyan
* ( 1.0 - fKey
)) + fKey
;
73 fMagenta
= (fMagenta
* ( 1.0 - fKey
)) + fKey
;
74 fYellow
= (fYellow
* ( 1.0 - fKey
)) + fKey
;
76 dR
= std::clamp( 1.0 - fCyan
, 0.0, 1.0 );
77 dG
= std::clamp( 1.0 - fMagenta
, 0.0, 1.0 );
78 dB
= std::clamp( 1.0 - fYellow
, 0.0, 1.0 );
81 // This function based on code under ALv2 - Copyright 2013 István Ujj-Mészáros
82 // credit Avisek Das and István Ujj-Mészáros
83 static void lcl_XYZtoRGB( float fX
, float fY
, float fZ
, float& dR
, float& dG
, float& dB
)
85 // Observer = 2°, Illuminant = D65
91 dR
= fX
* 3.2406 + fY
* -1.5372 + fZ
* -0.4986;
92 // Y from 0 to 100.000
93 dG
= fX
* -0.9689 + fY
* 1.8758 + fZ
* 0.0415;
94 // Z from 0 to 108.883
95 dB
= fX
* 0.0557 + fY
* -0.2040 + fZ
* 1.0570;
99 dR
= 1.055 * (std::pow(dR
, 0.41666667)) - 0.055;
108 dG
= 1.055 * (std::pow(dG
, 0.41666667)) - 0.055;
117 dB
= 1.055 * (std::pow(dB
, 0.41666667)) - 0.055;
128 // This function based on code under ALv2 - Copyright 2013 István Ujj-Mészáros
129 // credit Avisek Das and István Ujj-Mészáros
130 static void lcl_LABtoXYZ( float fL
, float fa
, float fb
, float& dX
, float& dY
, float& dZ
)
132 dY
= (fL
+ 16) / 116;
133 dX
= (fa
/ 500) + dY
;
134 dZ
= dY
- (fb
/ 200);
136 if (std::pow(dY
, 3) > 0.008856)
138 dY
= std::pow(dY
, 3);
142 dY
= (dY
- 0.137931034) / 7.787;
145 if (std::pow(dX
, 3) > 0.008856)
147 dX
= std::pow(dX
, 3);
151 dX
= (dX
- 0.137931034) / 7.787;
154 if (std::pow(dZ
, 3) > 0.008856)
156 dZ
= std::pow(dZ
, 3);
160 dZ
= (dZ
- 0.137931034) / 7.787;
163 // Observer = 2°, Illuminant = D65
169 static void lcl_LABtoRGB( float fL
, float fa
, float fb
, float& dR
, float& dG
, float& dB
)
172 lcl_LABtoXYZ(fL
, fa
, fb
, x
, y
, z
);
174 lcl_XYZtoRGB(x
, y
, z
, dR
, dG
, dB
);
177 void PaletteASE::LoadPalette()
179 SvFileStream
aFile(maFPath
, StreamMode::READ
);
180 aFile
.SetEndian(SvStreamEndian::BIG
);
182 // Verify magic first 4 characters
183 char cMagic
[5] = {0};
184 if ((aFile
.ReadBytes(cMagic
, 4) != 4) || (strncmp(cMagic
, "ASEF", 4) != 0))
186 mbValidPalette
= false;
190 // Ignore the version number
193 sal_uInt32 nBlocks
= 0;
194 aFile
.ReadUInt32(nBlocks
);
195 for (sal_uInt32 nI
= 0; nI
< nBlocks
; nI
++) {
196 sal_uInt32 nChunkType
= 0;
197 aFile
.ReadUInt32(nChunkType
);
202 // Grab chunk size, name length
203 sal_uInt16 nChunkSize
= 0;
204 sal_uInt16 nChars
= 0;
205 aFile
.ReadUInt16(nChunkSize
);
206 aFile
.ReadUInt16(nChars
);
208 OUString
aPaletteName(u
""_ustr
);
210 aPaletteName
= read_uInt16s_ToOUString(aFile
, nChars
);
214 if (nChunkType
== 0xC0010000)
216 // Got a start chunk, so set palette name
217 maASEPaletteName
= aPaletteName
;
218 // Is there color data? (shouldn't happen in a start block, but check anyway)
219 if (nChunkSize
> ((nChars
* 2) + 2))
220 aPaletteName
.clear();
225 char cColorModel
[5] = {0};
226 aFile
.ReadBytes(cColorModel
, 4);
227 OString
aColorModel(cColorModel
);
228 // r, g, and b are floats ranging from 0 to 1
229 float r
= 0, g
= 0, b
= 0;
231 if (aColorModel
.equalsIgnoreAsciiCase("cmyk"))
233 float c
= 0, m
= 0, y
= 0, k
= 0;
238 lcl_CMYKtoRGB(c
, m
, y
, k
, r
, g
, b
);
240 else if (aColorModel
.equalsIgnoreAsciiCase("rgb "))
246 else if (aColorModel
.equalsIgnoreAsciiCase("gray"))
249 aFile
.ReadFloat(nVal
);
252 else if (aColorModel
.equalsIgnoreAsciiCase("LAB "))
254 float fL
= 0, fA
= 0, fB
= 0;
258 lcl_LABtoRGB(fL
, fA
, fB
, r
, g
, b
);
263 maColors
.emplace_back(Color(r
* 255, g
* 255, b
* 255), aPaletteName
);
266 mbValidPalette
= true;
269 Palette
* PaletteASE::Clone() const
271 return new PaletteASE(*this);
274 // PaletteGPL ------------------------------------------------------------------
276 static OString
lcl_getToken(OStringBuffer
& rStr
, sal_Int32
& index
);
278 PaletteGPL::PaletteGPL( OUString aFPath
, OUString aFName
) :
279 mbLoadedPalette( false ),
280 mbValidPalette( false ),
281 maFName(std::move( aFName
)),
282 maFPath(std::move( aFPath
))
287 PaletteGPL::~PaletteGPL()
291 const OUString
& PaletteGPL::GetName()
293 return maGPLPaletteName
;
296 const OUString
& PaletteGPL::GetPath()
301 void PaletteGPL::LoadColorSet(SvxColorValueSet
& rColorSet
)
307 for (const auto& rColor
: maColors
)
309 rColorSet
.InsertItem(nIx
, rColor
.m_aColor
, rColor
.m_aName
);
314 bool PaletteGPL::IsValid()
316 return mbValidPalette
;
319 bool PaletteGPL::ReadPaletteHeader(SvFileStream
& rFileStream
)
322 std::string_view aPaletteName
;
324 rFileStream
.ReadLine(aLine
);
325 if( !aLine
.startsWith("GIMP Palette") ) return false;
326 rFileStream
.ReadLine(aLine
);
327 if( aLine
.startsWith("Name: ", &aPaletteName
) )
329 maGPLPaletteName
= OStringToOUString(aPaletteName
, RTL_TEXTENCODING_ASCII_US
);
330 rFileStream
.ReadLine(aLine
);
331 if( aLine
.startsWith("Columns: "))
332 rFileStream
.ReadLine(aLine
); // we can ignore this
336 maGPLPaletteName
= maFName
;
341 void PaletteGPL::LoadPaletteHeader()
343 SvFileStream
aFile(maFPath
, StreamMode::READ
);
344 mbValidPalette
= ReadPaletteHeader( aFile
);
347 void PaletteGPL::LoadPalette()
349 if( mbLoadedPalette
) return;
350 mbLoadedPalette
= true;
352 // TODO add error handling!!!
353 SvFileStream
aFile(maFPath
, StreamMode::READ
);
354 mbValidPalette
= ReadPaletteHeader( aFile
);
356 if( !mbValidPalette
) return;
363 if (aLine
[0] != '#' && aLine
[0] != '\n')
365 // TODO check if r,g,b are 0<= x <=255, or just clamp?
366 sal_Int32 nIndex
= 0;
369 token
= lcl_getToken(aLine
, nIndex
);
370 if(token
.isEmpty() || nIndex
== -1) continue;
371 sal_Int32 r
= token
.toInt32();
373 token
= lcl_getToken(aLine
, nIndex
);
374 if(token
.isEmpty() || nIndex
== -1) continue;
375 sal_Int32 g
= token
.toInt32();
377 token
= lcl_getToken(aLine
, nIndex
);
378 if(token
.isEmpty()) continue;
379 sal_Int32 b
= token
.toInt32();
381 std::string_view name
;
383 name
= std::string_view(aLine
).substr(nIndex
);
385 maColors
.emplace_back(
387 OStringToOUString(name
, RTL_TEXTENCODING_ASCII_US
));
389 } while (aFile
.ReadLine(aLine
));
392 Palette
* PaletteGPL::Clone() const
394 return new PaletteGPL(*this);
397 // finds first token in rStr from index, separated by whitespace
398 // returns position of next token in index
399 static OString
lcl_getToken(OStringBuffer
& rStr
, sal_Int32
& index
)
401 sal_Int32 substart
, toklen
= 0;
402 OUString
aWhitespaceChars( u
" \n\t"_ustr
);
404 while(index
< rStr
.getLength() &&
405 aWhitespaceChars
.indexOf( rStr
[index
] ) != -1)
407 if(index
== rStr
.getLength())
414 //counts length of token
415 while(index
< rStr
.getLength() &&
416 aWhitespaceChars
.indexOf( rStr
[index
] ) == -1 )
422 //counts to position of next token
423 while(index
< rStr
.getLength() &&
424 aWhitespaceChars
.indexOf( rStr
[index
] ) != -1 )
426 if(index
== rStr
.getLength())
429 return OString(std::string_view(rStr
).substr(substart
, toklen
));
432 // PaletteSOC ------------------------------------------------------------------
434 PaletteSOC::PaletteSOC( OUString aFPath
, OUString aFName
) :
435 mbLoadedPalette( false ),
436 maFPath(std::move( aFPath
)),
437 maSOCPaletteName(std::move( aFName
))
441 PaletteSOC::~PaletteSOC()
445 const OUString
& PaletteSOC::GetName()
447 return maSOCPaletteName
;
450 const OUString
& PaletteSOC::GetPath()
455 void PaletteSOC::LoadColorSet(SvxColorValueSet
& rColorSet
)
457 if( !mbLoadedPalette
)
459 mbLoadedPalette
= true;
460 mpColorList
= XPropertyList::AsColorList(XPropertyList::CreatePropertyListFromURL(XPropertyListType::Color
, maFPath
));
461 (void)mpColorList
->Load();
464 if( mpColorList
.is() )
465 rColorSet
.addEntriesForXColorList( *mpColorList
);
468 bool PaletteSOC::IsValid()
473 Palette
* PaletteSOC::Clone() const
475 return new PaletteSOC(*this);
478 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */