00001 // $Id: doccolor.cpp 1605 2006-07-29 19:26:03Z alex $ 00002 /* @@tag:xara-cn@@ DO NOT MODIFY THIS LINE 00003 ================================XARAHEADERSTART=========================== 00004 00005 Xara LX, a vector drawing and manipulation program. 00006 Copyright (C) 1993-2006 Xara Group Ltd. 00007 Copyright on certain contributions may be held in joint with their 00008 respective authors. See AUTHORS file for details. 00009 00010 LICENSE TO USE AND MODIFY SOFTWARE 00011 ---------------------------------- 00012 00013 This file is part of Xara LX. 00014 00015 Xara LX is free software; you can redistribute it and/or modify it 00016 under the terms of the GNU General Public License version 2 as published 00017 by the Free Software Foundation. 00018 00019 Xara LX and its component source files are distributed in the hope 00020 that it will be useful, but WITHOUT ANY WARRANTY; without even the 00021 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 00022 See the GNU General Public License for more details. 00023 00024 You should have received a copy of the GNU General Public License along 00025 with Xara LX (see the file GPL in the root directory of the 00026 distribution); if not, write to the Free Software Foundation, Inc., 51 00027 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 00028 00029 00030 ADDITIONAL RIGHTS 00031 ----------------- 00032 00033 Conditional upon your continuing compliance with the GNU General Public 00034 License described above, Xara Group Ltd grants to you certain additional 00035 rights. 00036 00037 The additional rights are to use, modify, and distribute the software 00038 together with the wxWidgets library, the wxXtra library, and the "CDraw" 00039 library and any other such library that any version of Xara LX relased 00040 by Xara Group Ltd requires in order to compile and execute, including 00041 the static linking of that library to XaraLX. In the case of the 00042 "CDraw" library, you may satisfy obligation under the GNU General Public 00043 License to provide source code by providing a binary copy of the library 00044 concerned and a copy of the license accompanying it. 00045 00046 Nothing in this section restricts any of the rights you have under 00047 the GNU General Public License. 00048 00049 00050 SCOPE OF LICENSE 00051 ---------------- 00052 00053 This license applies to this program (XaraLX) and its constituent source 00054 files only, and does not necessarily apply to other Xara products which may 00055 in part share the same code base, and are subject to their own licensing 00056 terms. 00057 00058 This license does not apply to files in the wxXtra directory, which 00059 are built into a separate library, and are subject to the wxWindows 00060 license contained within that directory in the file "WXXTRA-LICENSE". 00061 00062 This license does not apply to the binary libraries (if any) within 00063 the "libs" directory, which are subject to a separate license contained 00064 within that directory in the file "LIBS-LICENSE". 00065 00066 00067 ARRANGEMENTS FOR CONTRIBUTION OF MODIFICATIONS 00068 ---------------------------------------------- 00069 00070 Subject to the terms of the GNU Public License (see above), you are 00071 free to do whatever you like with your modifications. However, you may 00072 (at your option) wish contribute them to Xara's source tree. You can 00073 find details of how to do this at: 00074 http://www.xaraxtreme.org/developers/ 00075 00076 Prior to contributing your modifications, you will need to complete our 00077 contributor agreement. This can be found at: 00078 http://www.xaraxtreme.org/developers/contribute/ 00079 00080 Please note that Xara will not accept modifications which modify any of 00081 the text between the start and end of this header (marked 00082 XARAHEADERSTART and XARAHEADEREND). 00083 00084 00085 MARKS 00086 ----- 00087 00088 Xara, Xara LX, Xara X, Xara X/Xtreme, Xara Xtreme, the Xtreme and Xara 00089 designs are registered or unregistered trademarks, design-marks, and/or 00090 service marks of Xara Group Ltd. All rights in these marks are reserved. 00091 00092 00093 Xara Group Ltd, Gaddesden Place, Hemel Hempstead, HP2 6EX, UK. 00094 http://www.xara.com/ 00095 00096 =================================XARAHEADEREND============================ 00097 */ 00098 // doccolor.cpp - the DocColour class 00099 00100 /* 00101 */ 00102 00103 00104 #include "camtypes.h" 00105 00106 //#include "colcontx.h" 00107 #include "colormgr.h" 00108 #include "colourix.h" 00109 #include "colplate.h" 00110 //#include "doccolor.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00111 //#include "ensure.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00112 00113 00114 CC_IMPLEMENT_MEMDUMP(DocColour, CC_CLASS_MEMDUMP) 00115 00116 00117 #ifdef _DEBUG 00118 // --- Comment out the line below to disable IndexedColour usage tracing 00119 //#define TRACECOLOURIX 1 00120 // --- 00121 #endif 00122 00123 // Declare smart memory handling in Debug builds 00124 #define new CAM_DEBUG_NEW 00125 00126 /* DEFAULTCONTEXT(ColourModel) 00127 * Macro to find a pointer to the global default colour context for a given 00128 * Colour Model. 00129 * eg: DEFAULTCONTEXT(ColModel)->PackColour(...); 00130 */ 00131 #define DEFAULTCONTEXT(colmodel) (ColourContext::GetGlobalDefault((ColourModel)colmodel)) 00132 00133 /******************************************************************************************** 00134 00135 > void DocColour::InitialiseInfoField(void) 00136 00137 Author: Jason_Williams (Xara Group Ltd) <camelotdev@xara.com> 00138 Created: 30/03/94 00139 Inputs: - 00140 Outputs: - 00141 Returns: - 00142 Purpose: Private shared code for construction of DocColour objects 00143 Initialises the 'Info' structure to default values 00144 Scope: private 00145 Errors: - 00146 SeeAlso: - 00147 00148 ********************************************************************************************/ 00149 00150 void DocColour::InitialiseInfoField(ColourModel ColModel) 00151 { 00152 ENSURE((INT32) ColModel < MAX_COLOURMODELS, 00153 "Attempt to use illegal colour model!"); 00154 00155 Info.Reserved = 0; // Reserved - set to zero for safety 00156 00157 Info.OCContextHandle = 0; 00158 Info.SourceColourModel = ColModel; 00159 Info.CacheColourModel = COLOURMODEL_NOTCACHED; 00160 00161 Info.IsSeparable = TRUE; 00162 00163 Info.ForceRounding = FALSE; 00164 00165 Info.NoColour = FALSE; 00166 } 00167 00168 /******************************************************************************************** 00169 00170 > DocColour::DocColour() 00171 00172 Author: Jason_Williams (Xara Group Ltd) <camelotdev@xara.com> 00173 Created: 30/03/94 00174 Inputs: - 00175 Outputs: - 00176 Returns: - 00177 Purpose: Constructor for a DocColour 00178 Initialises the colour to an RGBT value representing 'no colour'. 00179 (i.e. The same as DocColour(COLOUR_TRANS)) 00180 Errors: - 00181 SeeAlso: - 00182 00183 ********************************************************************************************/ 00184 00185 DocColour::DocColour() 00186 { 00187 ZapSourceColour(); 00188 InitialiseInfoField(COLOURMODEL_RGBT); 00189 00190 SourceColour.RGBT.Red = 0; // Black & 100% Transparent 00191 SourceColour.RGBT.Green = 0; 00192 SourceColour.RGBT.Blue = 0; 00193 SourceColour.RGBT.Transparent = 255; 00194 00195 Info.NoColour = TRUE; // And also 'no colour' transparent 00196 } 00197 00198 00199 00200 /******************************************************************************************** 00201 00202 > DocColour::~DocColour() 00203 00204 Author: Jason_Williams (Xara Group Ltd) <camelotdev@xara.com> 00205 Created: 30/03/94 00206 Inputs: - 00207 Outputs: - 00208 Returns: - 00209 Purpose: Destructor for a DocColour 00210 Errors: - 00211 SeeAlso: - 00212 00213 ********************************************************************************************/ 00214 00215 DocColour::~DocColour() 00216 { 00217 // If we are a reference to an indexed colour, tell that colour that we are 00218 // no longer referencing it 00219 if (Info.SourceColourModel == COLOURMODEL_INDEXED) 00220 SourceColour.Indexed.Colour->DecrementUsage(); 00221 } 00222 00223 00224 00225 00226 00227 00228 00229 /******************************************************************************************** 00230 00231 > DocColour::DocColour(StockColour Col) 00232 00233 Author: Jason_Williams (Xara Group Ltd) <camelotdev@xara.com> 00234 Created: 06/04/94 00235 Inputs: Col - Identifies the stock colour to create 00236 Outputs: - 00237 Returns: - 00238 Purpose: Constructor for a DocColour 00239 Initialises it to the given predefined (stock) colours, as defined 00240 in stockcol.h (cf keyword StockColour) 00241 00242 Notes: ALL StockColours are non-separable colours! 00243 i.e. they should only be applied to user-interface rendering 00244 00245 Errors: - 00246 SeeAlso: StockColour 00247 00248 ********************************************************************************************/ 00249 00250 DocColour::DocColour(StockColour Col) 00251 { 00252 ZapSourceColour(); 00253 PColourValue Red = 0, Green = 0, Blue = 0, Transparent = 0; 00254 00255 InitialiseInfoField(COLOURMODEL_RGBT); 00256 00257 switch (Col) 00258 { 00259 case COLOUR_TRANS: 00260 Red = Green = Blue = Transparent = 255; 00261 Info.NoColour = TRUE; 00262 break; 00263 00264 case COLOUR_BLACK: 00265 case COLOUR_UNSELECTEDBLOB: 00266 case COLOUR_FILLDEFAULT: 00267 case COLOUR_LINEDEFAULT: 00268 case COLOUR_XORDRAG: 00269 break; 00270 00271 case COLOUR_DKGREY: 00272 Red = Green = Blue = 64; 00273 break; 00274 00275 case COLOUR_MIDGREY: 00276 Red = Green = Blue = 128; 00277 break; 00278 00279 case COLOUR_XOREDIT: 00280 Red = 64; 00281 Green = 64; 00282 Blue = 230; 00283 break; 00284 00285 case COLOUR_LTGREY: 00286 case COLOUR_BEZIERLINE: 00287 Red = Green = Blue = 192; 00288 break; 00289 00290 case COLOUR_WHITE: 00291 Red = Green = Blue = 255; 00292 break; 00293 00294 case COLOUR_RED: 00295 case COLOUR_XORSELECT: 00296 case COLOUR_TOOLBLOB: 00297 case COLOUR_SELECTEDBLOB: 00298 case COLOUR_BEZIERBLOB: 00299 Red = 255; 00300 break; 00301 00302 case COLOUR_GREEN: 00303 Green = 255; 00304 break; 00305 00306 case COLOUR_BLUE: 00307 case COLOUR_XORNEW: 00308 case COLOUR_GRID: 00309 Blue = 255; 00310 break; 00311 00312 case COLOUR_CYAN: 00313 Green = Blue = 255; 00314 break; 00315 00316 case COLOUR_MAGENTA: 00317 Red = Blue = 255; 00318 break; 00319 00320 case COLOUR_YELLOW: 00321 Red = Green = 255; 00322 break; 00323 } 00324 00325 00326 SourceColour.RGBT.Red = Red; 00327 SourceColour.RGBT.Green = Green; 00328 SourceColour.RGBT.Blue = Blue; 00329 SourceColour.RGBT.Transparent = Transparent; 00330 00331 Info.IsSeparable = FALSE; 00332 } 00333 00334 00335 00336 00337 /******************************************************************************************** 00338 00339 > DocColour::DocColour(ColourValue Red, ColourValue Green, ColourValue Blue) 00340 00341 Author: Jason_Williams (Xara Group Ltd) <camelotdev@xara.com> 00342 Created: 30/03/94 00343 Inputs: Red; Green; Blue - The RGB definition of the colour 00344 Outputs: - 00345 Returns: - 00346 Purpose: Constructor for a DocColour 00347 Initialises it to the given RGBT value. 00348 Values are ColourValues (FIXED24s) in the range 0.0 to 1.0 00349 00350 Notes: DocColours no longer support transparency/transtype. 00351 00352 Errors: - 00353 SeeAlso: - 00354 00355 ********************************************************************************************/ 00356 00357 DocColour::DocColour(ColourValue Red, ColourValue Green, ColourValue Blue) 00358 { 00359 ZapSourceColour(); 00360 ColourRGBT temp; 00361 00362 InitialiseInfoField( COLOURMODEL_RGBT ); 00363 00364 temp.Red = Red; // Copy data into 128-bit struct, then pack 00365 temp.Green = Green; // it into our 32-bit store. 00366 temp.Blue = Blue; 00367 temp.Transparent = 0; 00368 00369 DEFAULTCONTEXT(COLOURMODEL_RGBT)->PackColour( (ColourGeneric *)&temp, &SourceColour ); 00370 } 00371 00372 00373 00374 /******************************************************************************************** 00375 00376 > DocColour::DocColour(ColourModel ColModel, ColourGeneric *Col) 00377 00378 Author: Jason_Williams (Xara Group Ltd) <camelotdev@xara.com> 00379 Created: 30/03/94 00380 Inputs: ColModel - Colour model in which Col is defined 00381 Col - definition of the colour in model ColModel 00382 Outputs: - 00383 Returns: - 00384 Purpose: Constructor for a DocColour 00385 Initialises it to the given value 00386 00387 Notes: Note the existence of the macros: 00388 DOCCOLOUR_RGBT(Colour_RGBT *col) 00389 DOCCOLOUR_CMYK(Colour_CMYK *col) 00390 DOCCOLOUR_HSVT(Colour_HSVT *col) 00391 DOCCOLOUR_CIET(COLOUR_CIET *col) 00392 which can be used to create DocColours from ColourRGBT etc structs 00393 e.g. 00394 ColourRGBT AnRGBTColourDefn; 00395 DocColour MyRGBT = DOCCOLOUR_RGBT(&AnRGBTColourDefn); 00396 These macros are defined in doccolor.h 00397 00398 Errors: - 00399 SeeAlso: - 00400 00401 ********************************************************************************************/ 00402 00403 DocColour::DocColour(ColourModel ColModel, ColourGeneric *Col) 00404 { 00405 ZapSourceColour(); 00406 InitialiseInfoField(ColModel); 00407 DEFAULTCONTEXT(ColModel)->PackColour(Col, &SourceColour); 00408 } 00409 00410 00411 00412 /******************************************************************************************** 00413 00414 > DocColour::DocColour(const DocColour &other); 00415 00416 Author: Jason_Williams (Xara Group Ltd) <camelotdev@xara.com> 00417 Created: 14/04/94 00418 Inputs: Other - colour to copy 00419 Outputs: - 00420 Returns: 00421 Purpose: DocColour copy constructor 00422 Notes: If copying a reference to an indexed colour, the indexed colours 00423 usage count will be incremented. 00424 If this is already a reference to an indexed colour, that indexed 00425 colours usage count will be decremented. 00426 Errors: - 00427 00428 ********************************************************************************************/ 00429 00430 DocColour::DocColour(const DocColour &Other) 00431 { 00432 ENSURE(Other.Info.SourceColourModel < MAX_COLOURMODELS, 00433 "Attempt to use illegal colour model!"); 00434 00435 Info = Other.Info; 00436 CachedColour = Other.CachedColour; 00437 SourceColour = Other.SourceColour; 00438 00439 if (Info.SourceColourModel == COLOURMODEL_INDEXED) 00440 { 00441 ENSURE(SourceColour.Indexed.Colour != NULL, 00442 "Corrupted DocColour (NULL IndexedColour ref) detected"); 00443 00444 SourceColour.Indexed.Colour->IncrementUsage(); 00445 ENSURE(Info.CacheColourModel == 0, "RefToIndexedColour DocColour has a cache!!"); 00446 00447 #ifdef TRACECOLOURIX 00448 TRACE( _T(">> Construct DocColour(DocColour => ColourIx %x) @ %x\n"), (INT32)SourceColour.Indexed.Colour, (INT32)this); 00449 #endif 00450 } 00451 } 00452 00453 00454 00455 /******************************************************************************************** 00456 00457 > DocColour::DocColour(IndexedColour *Col) 00458 00459 Author: Jason_Williams (Xara Group Ltd) <camelotdev@xara.com> 00460 Created: 30/03/94 00461 Purpose: {Copy} Constructor for a DocColour 00462 Notes: THIS FUNCTION DOES NOT EXIST!! 00463 DocColour::MakeRefToIndexedColour() should be used if you wish 00464 to "assign" an IndexedColour to a DocColour. 00465 SeeAlso: DocColour::MakeRefToIndexedColour 00466 00467 ********************************************************************************************/ 00468 00469 00470 /******************************************************************************************** 00471 00472 > ColourModel DocColour::GetColourModel(void) const 00473 00474 Author: Jason_Williams (Xara Group Ltd) <camelotdev@xara.com> 00475 Created: 30/03/94 00476 Inputs: - 00477 Outputs: - 00478 Returns: A Colour Model identifying the model in which the colour is defined 00479 Purpose: To determine the colourmodel in which the given colour was defined 00480 Notes: If this is a reference to an IndexedColour, returns the colour of the 00481 referenced IndexedColour. 00482 The colour model number returned may be used as an index into the list 00483 of ColourContexts held by each document, in order to find a relevant 00484 context for the colour. 00485 Errors: - 00486 SeeAlso: DocColour::GetSourceColour 00487 00488 ********************************************************************************************/ 00489 00490 ColourModel DocColour::GetColourModel(void) const 00491 { 00492 ENSURE(Info.SourceColourModel < MAX_COLOURMODELS, 00493 "DocColour based on illegal colour model!"); 00494 00495 // If colour is a reference to an indexed colour, return indexed colour's 00496 // colour model 00497 if (Info.SourceColourModel == COLOURMODEL_INDEXED) 00498 { 00499 ENSURE(SourceColour.Indexed.Colour != NULL, 00500 "Corrupted DocColour (NULL IndexedColour ref) detected"); 00501 return(SourceColour.Indexed.Colour->GetColourModel()); 00502 } 00503 00504 return((ColourModel) Info.SourceColourModel); 00505 } 00506 00507 00508 00509 00510 /******************************************************************************************** 00511 00512 > void DocColour::GetSourceColour(ColourGeneric *Result) 00513 00514 Author: Jason_Williams (Xara Group Ltd) <camelotdev@xara.com> 00515 Created: 30/03/94 00516 Inputs: Result - pointer to structure to recieve the colour 00517 Outputs: - 00518 Returns: The native definition of the colour. This is a ColourPacked containing 00519 the colour as defined in its original Colour Model (cf DocColour::GetColourModel) 00520 Purpose: Determine the native definition of the given colour 00521 Notes: If this is a reference to an IndexedColour, returns the colour of the 00522 referenced IndexedColour. 00523 Errors: - 00524 SeeAlso: DocColour::GetColourModel 00525 00526 ********************************************************************************************/ 00527 00528 void DocColour::GetSourceColour(ColourGeneric *Result) 00529 { 00530 ENSURE(Info.SourceColourModel < MAX_COLOURMODELS, 00531 "DocColour based on illegal colour model!"); 00532 00533 // If colour is reference to indexed colour, ask the indexed colour for the 00534 // original colour definition, else return our own definition. 00535 if (Info.SourceColourModel == COLOURMODEL_INDEXED) 00536 { 00537 ENSURE(SourceColour.Indexed.Colour != NULL, 00538 "Corrupted DocColour (NULL IndexedColour ref) detected"); 00539 SourceColour.Indexed.Colour->GetSourceColour(Result); 00540 } 00541 else 00542 DEFAULTCONTEXT(Info.SourceColourModel)-> 00543 UnpackColour(&SourceColour, Result); 00544 00545 if (Info.ForceRounding) 00546 { 00547 // Our ForceRounding flag is on, so we must round all components to the closest 00548 // half percentage value. This is a bodge to correct for representational errors 00549 // in PANTONE library colours. 00550 double temp; 00551 00552 temp = Result->Component1.MakeDouble(); 00553 temp = (floor((temp * 200.0) + 0.5)) / 200.0; // Round to nearest 200th 00554 Result->Component1 = temp; 00555 00556 temp = Result->Component2.MakeDouble(); 00557 temp = (floor((temp * 200.0) + 0.5)) / 200.0; // Round to nearest 200th 00558 Result->Component2 = temp; 00559 00560 temp = Result->Component3.MakeDouble(); 00561 temp = (floor((temp * 200.0) + 0.5)) / 200.0; // Round to nearest 200th 00562 Result->Component3 = temp; 00563 00564 temp = Result->Component4.MakeDouble(); 00565 temp = (floor((temp * 200.0) + 0.5)) / 200.0; // Round to nearest 200th 00566 Result->Component4 = temp; 00567 } 00568 } 00569 00570 00571 00572 /******************************************************************************************** 00573 00574 > void DocColour::SetSeparable(BOOL IsSeparable = TRUE) 00575 00576 Author: Jason_Williams (Xara Group Ltd) <camelotdev@xara.com> 00577 Created: 30/03/94 00578 Inputs: IsSeparable - TRUE if this colour will be allowed to be separated and 00579 colour-corrected during conversions 00580 FALSE if this colour shouldn't be separated 00581 00582 Purpose: To set the separation/correction enable flag for a DocColour. 00583 Some colours (e.g. EOR blob colours) should not be colour-separated 00584 on screen. By default, all colours (except StockCols) will separate, 00585 but this flag can be set on "UI colours" to stop them separating. 00586 00587 SeeAlso: DocColour::IsSeparable 00588 00589 ********************************************************************************************/ 00590 00591 void DocColour::SetSeparable(BOOL IsSeparable) 00592 { 00593 Info.IsSeparable = IsSeparable; 00594 } 00595 00596 00597 /******************************************************************************************** 00598 00599 > void DocColour::SetReservedFlag(UINT32 Value) 00600 00601 Author: Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com> 00602 Created: 12/4/2000 00603 Inputs: Value - the value to set the flag to 00604 00605 Purpose: To set the Reserved flag of the colour info struct. 00606 00607 SeeAlso: My use of this flag can be seen in LineDefinition::ConvertIndexedColours and 00608 PathProcessorBrush::RenderAttributes. If you want to use this flag for your 00609 own purposes I suggest that you take a look at what I've done to make sure 00610 there will be no conflict. 00611 00612 ********************************************************************************************/ 00613 00614 void DocColour::SetReservedFlag(UINT32 Value) 00615 { 00616 Info.Reserved = Value; 00617 } 00618 00619 00620 /******************************************************************************************** 00621 00622 > UINT32 DocColour::GetReservedFlag() 00623 00624 Author: Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com> 00625 Created: 12/4/2000 00626 Inputs: - 00627 Returns: the Reserved Flag of the ColourInfo struct 00628 Purpose: access fn. 00629 00630 SeeAlso: 00631 00632 ********************************************************************************************/ 00633 00634 UINT32 DocColour::GetReservedFlag() 00635 { 00636 return Info.Reserved; 00637 } 00638 00639 00640 /******************************************************************************************** 00641 00642 > BOOL DocColour::IsNamed() 00643 00644 Author: Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com> 00645 Created: 12/4/2000 00646 Inputs: - 00647 Returns: TRUE, if this colour is named, or if it has the flag set that indicates that it 00648 was created by a named colour 00649 Purpose: to identidy if this colour is named, or was created from a named colour. Note that 00650 not all colours that were created by Named colours will have the relevant flag set, as 00651 I have only just started using it (4/2000). This fn. was really designed to be used 00652 specifically by the brush attribute replace named colours system. 00653 00654 SeeAlso: 00655 00656 ********************************************************************************************/ 00657 00658 BOOL DocColour::IsNamed() 00659 { 00660 BOOL RetVal = FALSE; 00661 00662 // first try the parent colour 00663 IndexedColour* pParent = FindParentIndexedColour(); 00664 if (pParent != NULL) 00665 { 00666 if (pParent->IsNamed()) 00667 RetVal = TRUE; 00668 } 00669 00670 // now try the flags 00671 if (Info.Reserved == COL_NAMED) 00672 RetVal = TRUE; 00673 00674 return RetVal; 00675 } 00676 00677 00678 /******************************************************************************************** 00679 00680 > void DocColour::HackColReplacerPreDestruct() 00681 00682 Author: Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com> 00683 Created: 10/6/2000 00684 Inputs: - 00685 Returns: - 00686 Purpose: As you might expect from the name this is a real bodge. Basically the named 00687 colour replacer used in the brush needs to store some colours which are 00688 converted index colours. 00689 To cut a long story short I'm looking at a deadline and this prevents an 00690 assert upon destruction, theres no reason for anyone else to use it. 00691 00692 SeeAlso: 00693 00694 ********************************************************************************************/ 00695 00696 void DocColour::HackColReplacerPreDestruct() 00697 { 00698 Info.SourceColourModel = MAX_COLOURMODELS; 00699 } 00700 00701 /******************************************************************************************** 00702 00703 > void DocColour::Mix(DocColour *BlendStart, DocColour *BlendEnd, double BlendFraction, 00704 ColourContext *BlendContext, BOOL BlendTheLongWay = FALSE, 00705 ColourContext *OutputContext = NULL) 00706 00707 Author: Jason_Williams (Xara Group Ltd) <camelotdev@xara.com> 00708 Created: 11/11/94 (moved from constructor, 29/5/96) 00709 00710 Inputs: BlendStart - The start DocColour 00711 00712 BlendEnd - The end DocColour 00713 00714 BlendFraction - The amount by which the two colours should be blended, 00715 in the range 0.0 to 1.0. Blending is linear in the given colourspace, 00716 achieved by the equation 00717 00718 Result = (BlendStart * (1.0-BlendFraction)) + (BlendEnd * BlendFraction) 00719 00720 BlendContext - the colour context in which the blend will take place 00721 (may be NULL, but this is NOT recommended) 00722 (Note that this is ignored if either colour is a Spot) 00723 (Note that if both end colours are CMYK, and Mix effect 00724 is used, a CMYK context is used instead, so that CMYK 00725 grads colour separate properly) 00726 00727 BlendTheLongWay - (ONLY used if the BlendContext points to an HSV context) 00728 This indicates whether to do 'rainbow' (shortest distance- FALSE) or 00729 'alt-rainbow' (longest distance- TRUE) HSV colour blending. 00730 00731 OutputContext - Normally, this should be NULL. However, if you're doing 00732 a colour separation involving spot colours, you should pass the output 00733 colour context in here, so that spots can be correctly separated. The 00734 output for mixing a spot colour will then be a greyscale ink intensity, 00735 correctly colour separated, and with its IsSeparable flag FALSE so that 00736 it is not colour separated or corrected a second time when output. It is 00737 safe to always pass the output context in here, as this function will 00738 always work out the appropriate behviour. 00739 00740 Purpose: "Constructor" for a DocColour - generates a blended DocColour 00741 The start and end colours are blended to create this new DocColour. The 00742 amount by which they are mixed is determined by BlendFraction. The blend 00743 occurs in the colourspace described by BlendContext. Note that BlendContext 00744 may be NULL, in which case the default context for the BlendStart colour 00745 will be used. However, this is not recommended, as this will result in 00746 a blend which may be incorrect for the context(s) in use in a given 00747 document, which could give seriously wonky colours! 00748 00749 If blending is requested in HSV space, the extra parameter BlendTheLongWay 00750 is required, to determine if blending is done in a "Rainbow" or "Alt-Rainbow" 00751 fashion. 00752 00753 Notes: Note that although this may be slow, you must mix colours using this 00754 method, or colour correction/separation may not occur. 00755 00756 The blend method specified by BlendContext & BlendTheLongWay is ignored 00757 if either of the colours is a spot colour - these are always coerced 00758 into simple "Mix" fills/blends, as otherwise the printed output and 00759 colour representation on screen will be totally different! 00760 00761 If both end colours reference the same IndexedColour, and the mixing 00762 method is Mix (RGB) or Rainbow (HSV, BlendTheLongWay==FALSE), then all 00763 intermediate colours are identical, and are thus returned referencing the 00764 original indexed colour. 00765 00766 If both the end colours are tints of the same spot colour, then two things 00767 can occur: 00768 * If OutputContext != NULL, we presume the mix is temporary for rendering 00769 to that context, so produce an RGB mix or separated Ink density value 00770 for the context. 00771 * If OutputContext == NULL, we presume you're doing a make shapes on 00772 a blend or similar, so we create a new local colour for each mix, 00773 which is a permanent proper tint of the parent spot ink. 00774 00775 ********************************************************************************************/ 00776 00777 void DocColour::Mix(DocColour *BlendStart, DocColour *BlendEnd, double BlendFraction, 00778 ColourContext *BlendContext, BOOL BlendTheLongWay, 00779 ColourContext *OutputContext) 00780 { 00781 // if (BlendFraction >= 1.0) 00782 // BlendFraction = 0.999999999; 00783 00784 // ERROR3IF(BlendFraction < 0.0 || BlendFraction > 1.0, 00785 // "DocColour::DocColour Blend: Illegal Blend fraction!"); 00786 00787 ERROR3IF(BlendStart == NULL || BlendEnd == NULL, 00788 "DocColour::DocColour Blend: NULL input parameters!"); 00789 00790 // Make sure that this colour is not a reference to an IndexedColour, otherwise 00791 // if/when we InitialiseInfoField() below, we'll forget the reference, and trigger 00792 // lots of bogus "IndexedColour deleted while in use" warnings 00793 if (Info.SourceColourModel == COLOURMODEL_INDEXED) 00794 { 00795 SourceColour.Indexed.Colour->DecrementUsage(); 00796 Info.SourceColourModel = COLOURMODEL_RGBT; 00797 } 00798 00799 // If either the start of end colour is a spot/tint colour, then we will coerce 00800 // this mix into a simple RGB mix, not a Hue mix, by grabbing the default RGB context 00801 // We also special-case a Mix/Rainbow from an IndexedColour to itself, by simply making 00802 // the mixed step another reference to that colour (but not for alt-rainbow!) 00803 BOOL IncludesSpots = FALSE; // Remember if we've got to worry about spots 00804 00805 IndexedColour *StartParent = BlendStart->FindParentIndexedColour(); 00806 IndexedColour *EndParent = BlendEnd->FindParentIndexedColour(); 00807 00808 00809 // --- Are blend start/end colours identical? - we might optimise them out if they are 00810 if (StartParent != NULL && StartParent == EndParent) 00811 { 00812 // If it's an RGB Mix or an HSV Rainbow (short way) then all mixes are identical to 00813 // the original colour, so just return that instead. 00814 if ( BlendContext != NULL && 00815 ( BlendContext->GetColourModel () == COLOURMODEL_RGBT || 00816 ( BlendContext->GetColourModel () == COLOURMODEL_HSVT && !BlendTheLongWay ) ) ) 00817 { 00818 // Make ourselves a reference to the parent colour 00819 MakeRefToIndexedColour ( StartParent ); 00820 return; 00821 } 00822 } 00823 00824 00825 // --- Determine if one or both of the colours are spot/tint colours 00826 // (When separating, we must treat blends including spot colours very specially) 00827 if (StartParent != NULL && StartParent->IsSpotOrTintOfSpot()) 00828 IncludesSpots = TRUE; 00829 else if (EndParent != NULL && EndParent->IsSpotOrTintOfSpot()) 00830 IncludesSpots = TRUE; 00831 00832 if (IncludesSpots) 00833 { 00834 // The blend includes spot colours, so must be coerced into an RGB "Mix" 00835 BlendContext = ColourManager::GetColourContext(COLOURMODEL_RGBT); 00836 00837 IndexedColour *StartSpot = BlendStart->GetSpotParent(); 00838 IndexedColour *EndSpot = BlendEnd->GetSpotParent(); 00839 00840 if (OutputContext == NULL) 00841 { 00842 // Doing Make Shapes, so should make permanent IndexedColour tints, if 00843 // both the end colours are tints of the same spot colour 00844 if (StartSpot != NULL && StartSpot == EndSpot) 00845 { 00846 MixTint(StartSpot, StartParent, EndParent, BlendFraction); 00847 return; 00848 } 00849 // else drop through to normal RGB mixing code (which means the colours 00850 // become process colours. Damn! -- but there's nothing we can do about it) 00851 } 00852 else 00853 { 00854 ColourPlate *ColPlate = OutputContext->GetColourPlate(); 00855 00856 if (ColPlate != NULL && !ColPlate->IsDisabled()) 00857 { 00858 // Make us into a CMYK with the ink value in our Key component 00859 // The Key is calculated below (in the if statement) but we'll do the common stuff here 00860 InitialiseInfoField(COLOURMODEL_CMYK); 00861 SourceColour.CMYK.Cyan = SourceColour.CMYK.Magenta = 00862 SourceColour.CMYK.Yellow = 0; 00863 00864 if (ColPlate->GetType() == COLOURPLATE_SPOT) 00865 { 00866 // We're rendering to a spot plate, so we want to output an ink density value. 00867 // Thus, we create a CMYK value, and place the ink density in the Key component. 00868 // We will then set it non-separable so that it is not separated a second time 00869 // (see after this if statement) 00870 // (NOTE: IF both ends are tints of this spot plate, we'll interpolate the tint. If 00871 // only one end is a spot colour, then the other end will become a 0% tint, i.e. white) 00872 00873 // Duh! We need to check which spot plate we are doing or spot colours end up 00874 // being printed on all spot plates! 00875 00876 IndexedColour* pPlateSpot = ColPlate->GetSpotColour(); 00877 00878 // Work out the start and end tint values for use with the common ancestor (if any). 00879 double StartValue = 0.0; 00880 if (StartSpot != NULL && StartSpot == pPlateSpot) 00881 StartValue = StartParent->GetAccumulatedTintValue().MakeDouble(); 00882 00883 double EndValue = 0.0; 00884 if (EndSpot != NULL && EndSpot == pPlateSpot) 00885 EndValue = EndParent->GetAccumulatedTintValue().MakeDouble(); 00886 00887 // Linearly interpolate the tint values 00888 double NewTint = (StartValue * (1.0 - BlendFraction)) + (EndValue * (BlendFraction)); 00889 00890 SourceColour.CMYK.Key = (PColourValue) (NewTint * 255.0); 00891 } 00892 else 00893 { 00894 // Generate separated RGB versions of the end colours 00895 ColourRGBT SeparatedStart; 00896 if (BlendStart->IsTransparent()) 00897 OutputContext->GetWhite((ColourGeneric *) &SeparatedStart); 00898 else 00899 OutputContext->ConvertColour(BlendStart, (ColourGeneric *) &SeparatedStart); 00900 00901 ColourRGBT SeparatedEnd; 00902 if (BlendEnd->IsTransparent()) 00903 OutputContext->GetWhite((ColourGeneric *) &SeparatedEnd); 00904 else 00905 OutputContext->ConvertColour(BlendEnd, (ColourGeneric *) &SeparatedEnd); 00906 00907 00908 if (ColPlate->IsMonochrome()) 00909 { 00910 // We're doing a monochrome sep 00911 // The separated colour should be a greyscale, but let's just check 00912 ERROR3IF(SeparatedStart.Red != SeparatedStart.Green || 00913 SeparatedStart.Red != SeparatedStart.Blue, 00914 "Separated colour is not a greyscale! Doh!"); 00915 00916 ERROR3IF(SeparatedEnd.Red != SeparatedEnd.Green || 00917 SeparatedEnd.Red != SeparatedEnd.Blue, 00918 "Separated colour is not a greyscale! Doh!"); 00919 00920 // Assuming that R==G==B, use the R levels as ink intensities, and generate 00921 // this into the Key component 00922 double temp; 00923 BlendFraction *= 255.0; 00924 temp = SeparatedStart.Red.MakeDouble() * (255.0 - BlendFraction); 00925 temp += SeparatedEnd.Red.MakeDouble() * BlendFraction; 00926 SourceColour.CMYK.Key = 255 - ((PColourValue) (temp + 0.5)); 00927 } 00928 else 00929 { 00930 // We're doing a colour (e.g. screen preview in shades of Cyan or whatever) 00931 // separation, so just mix the colour in RGB for previewing) 00932 00933 // Override the CMYK setting we just did above! 00934 InitialiseInfoField(COLOURMODEL_RGBT); 00935 00936 // Now, generate an RGB mixed value 00937 double temp; 00938 BlendFraction *= 255.0; 00939 temp = SeparatedStart.Red.MakeDouble() * (255.0 - BlendFraction); 00940 temp += SeparatedEnd.Red.MakeDouble() * BlendFraction; 00941 SourceColour.RGBT.Red = (PColourValue) (temp + 0.5); 00942 00943 temp = SeparatedStart.Green.MakeDouble() * (255.0 - BlendFraction); 00944 temp += SeparatedEnd.Green.MakeDouble() * BlendFraction; 00945 SourceColour.RGBT.Green = (PColourValue) (temp + 0.5); 00946 00947 temp = SeparatedStart.Blue.MakeDouble() * (255.0 - BlendFraction); 00948 temp += SeparatedEnd.Blue.MakeDouble() * BlendFraction; 00949 SourceColour.RGBT.Blue = (PColourValue) (temp + 0.5); 00950 } 00951 } 00952 00953 // Finally, set ourselves non-separable so that we are not colour corrected 00954 // or separated a second time during output! 00955 SetSeparable(FALSE); 00956 00957 // Let's get out of here before we're spotted! (sorry, last bad joke, I promise) 00958 return; 00959 } 00960 // else drop through to do a normal RGB mix 00961 } 00962 // else drop through to do a normal RGB mix 00963 } 00964 00965 // --- Check for CMYK -> CMYK fill 00966 // If we're doing an RGB mix and both colours are CMYK, then we will use CMYK mixing 00967 // instead of RGB, so that CMYK grads separate properly 00968 if (BlendContext != NULL && BlendContext->GetColourModel() == COLOURMODEL_RGBT && 00969 BlendStart->GetColourModel() == COLOURMODEL_CMYK && 00970 BlendEnd->GetColourModel() == COLOURMODEL_CMYK) 00971 { 00972 BlendContext = ColourManager::GetColourContext(COLOURMODEL_CMYK); 00973 } 00974 00975 00976 // OK, so we're not picking at our spots (I lied about that being the last bad joke). 00977 // In that case, do a normal mix 00978 if (BlendContext == NULL) 00979 { 00980 BlendContext = ColourManager::GetColourContext(BlendStart->GetColourModel()); 00981 ERROR3IF(BlendContext == NULL, "Can't find a default ColourContext for blend colour space"); 00982 } 00983 00984 switch(BlendContext->GetColourModel()) 00985 { 00986 case COLOURMODEL_RGBT: 00987 MixRGB(BlendStart, BlendEnd, BlendFraction, BlendContext); 00988 break; 00989 00990 case COLOURMODEL_CMYK: 00991 MixCMYK(BlendStart, BlendEnd, BlendFraction, BlendContext); 00992 break; 00993 00994 case COLOURMODEL_HSVT: 00995 MixHSV(BlendStart, BlendEnd, BlendFraction, BlendContext, BlendTheLongWay); 00996 break; 00997 00998 default: 00999 ERROR3("DocColour::Mix unimplemented for BlendContext other then RGB or HSV"); 01000 break; 01001 } 01002 01003 // Finally, if both the start and end colour were non-separable, all intermediate 01004 // blended colours will also come out non-separable. This only usually has an effect with 01005 // the "print on all plates" attribute, which overrides the rendering colour with 01006 // special pre-separated greyscales 01007 if (!BlendStart->IsSeparable() && !BlendEnd->IsSeparable()) 01008 SetSeparable(FALSE); 01009 } 01010 01011 01012 01013 /******************************************************************************************** 01014 01015 > void DocColour::MixRGB(DocColour *BlendStart, DocColour *BlendEnd, double BlendFraction, 01016 ColourContext *BlendContext, ColourContext *OutputContext) 01017 01018 Author: Jason_Williams (Xara Group Ltd) <camelotdev@xara.com> 01019 Created: 12/6/96 01020 01021 Inputs: BlendStart - The start DocColour 01022 BlendEnd - The end DocColour 01023 BlendFraction - Blending amount between 0.0 and 1.0 01024 BlendContext - the RGB colour context in which the blend will take place 01025 01026 Purpose: Internal helper function for DocColour::Mix. 01027 This is a reasonably optimised routine for mixing RGB colours. 01028 01029 SeeAlso: DocColour::Mix 01030 01031 ********************************************************************************************/ 01032 01033 void DocColour::MixRGB(DocColour *BlendStart, DocColour *BlendEnd, double BlendFraction, 01034 ColourContext *BlendContext) 01035 { 01036 ERROR3IF(BlendStart == NULL || BlendEnd == NULL || BlendContext == NULL, "Illegal NULL params"); 01037 ERROR3IF(BlendContext->GetColourModel() != COLOURMODEL_RGBT, 01038 "DocColour::MixRGB is optimised for RGB colours only!"); 01039 01040 InitialiseInfoField(COLOURMODEL_RGBT); 01041 01042 ColourGeneric StartDef; 01043 if (BlendStart->IsTransparent()) // If no-colour, then... 01044 { 01045 BlendContext->GetWhite(&StartDef); // ... use white 01046 01047 if (BlendEnd->IsTransparent()) // If both ends no-colour, return no-colour 01048 Info.NoColour = TRUE; 01049 } 01050 else 01051 BlendContext->ConvertColour(BlendStart, &StartDef); // else use start colour 01052 01053 // If 0.0 then StartDef now has the colour to use (no blending necessary) 01054 // If BOTH colours are transparent, we will be NoColour, so no need to blend 01055 if (BlendFraction > 0.0 && !Info.NoColour) 01056 { 01057 ColourGeneric EndDef; 01058 if (BlendEnd->IsTransparent()) // If no-colour then use white 01059 BlendContext->GetWhite(&EndDef); 01060 else 01061 BlendContext->ConvertColour(BlendEnd, &EndDef); 01062 01063 // Precalculate the blending fractions. Optimisation - Multiply by 255 here to save 01064 // doing it for each component when we convert them into bytes below. 01065 BlendFraction *= 255.0; 01066 const double InverseFraction = (255.0 - BlendFraction); 01067 01068 double temp; 01069 01070 // Mix the RGB components into a 0..255 byte, and store into our packed colour definition 01071 temp = StartDef.Component1.MakeDouble() * InverseFraction; 01072 temp += EndDef.Component1.MakeDouble() * BlendFraction; 01073 SourceColour.RGBT.Red = (PColourValue) (temp + 0.5); 01074 01075 temp = StartDef.Component2.MakeDouble() * InverseFraction; 01076 temp += EndDef.Component2.MakeDouble() * BlendFraction; 01077 SourceColour.RGBT.Green = (PColourValue) (temp + 0.5); 01078 01079 temp = StartDef.Component3.MakeDouble() * InverseFraction; 01080 temp += EndDef.Component3.MakeDouble() * BlendFraction; 01081 SourceColour.RGBT.Blue = (PColourValue) (temp + 0.5); 01082 01083 // We don't bother with component 4, as RGB doesn't use it 01084 } 01085 else 01086 { 01087 // Pack the start colour straight into our SourceColour 01088 BlendContext->PackColour(&StartDef, &SourceColour); 01089 } 01090 } 01091 01092 01093 01094 /******************************************************************************************** 01095 01096 > void DocColour::MixCMYK(DocColour *BlendStart, DocColour *BlendEnd, double BlendFraction, 01097 ColourContext *BlendContext, ColourContext *OutputContext) 01098 01099 Author: Jason_Williams (Xara Group Ltd) <camelotdev@xara.com> 01100 Created: 12/6/96 01101 01102 Inputs: BlendStart - The start DocColour 01103 BlendEnd - The end DocColour 01104 BlendFraction - Blending amount between 0.0 and 1.0 01105 BlendContext - the RGB colour context in which the blend will take place 01106 01107 Purpose: Internal helper function for DocColour::Mix. 01108 This is a reasonably optimised routine for mixing CMYK colours. 01109 01110 SeeAlso: DocColour::Mix 01111 01112 ********************************************************************************************/ 01113 01114 void DocColour::MixCMYK(DocColour *BlendStart, DocColour *BlendEnd, double BlendFraction, 01115 ColourContext *BlendContext) 01116 { 01117 ERROR3IF(BlendStart == NULL || BlendEnd == NULL || BlendContext == NULL, "Illegal NULL params"); 01118 ERROR3IF(BlendContext->GetColourModel() != COLOURMODEL_CMYK, 01119 "DocColour::MixRGB is optimised for CMYK colours only!"); 01120 01121 InitialiseInfoField(COLOURMODEL_CMYK); 01122 01123 ColourGeneric StartDef; 01124 if (BlendStart->IsTransparent()) // If no-colour, then... 01125 { 01126 BlendContext->GetWhite(&StartDef); // ... use white 01127 01128 if (BlendEnd->IsTransparent()) // If both ends no-colour, return no-colour 01129 Info.NoColour = TRUE; 01130 } 01131 else 01132 BlendContext->ConvertColour(BlendStart, &StartDef); // else use start colour 01133 01134 // If 0.0 then StartDef now has the colour to use (no blending necessary) 01135 // If BOTH colours are transparent, we will be NoColour, so no need to blend 01136 if (BlendFraction > 0.0 && !Info.NoColour) 01137 { 01138 ColourGeneric EndDef; 01139 if (BlendEnd->IsTransparent()) // If no-colour then use white 01140 BlendContext->GetWhite(&EndDef); 01141 else 01142 BlendContext->ConvertColour(BlendEnd, &EndDef); 01143 01144 // Precalculate the blending fractions. Optimisation - Multiply by 255 here to save 01145 // doing it for each component when we convert them into bytes below. 01146 BlendFraction *= 255.0; 01147 const double InverseFraction = (255.0 - BlendFraction); 01148 01149 double temp; 01150 01151 // Mix the CMYK components into a 0..255 byte, and store into our packed colour definition 01152 temp = StartDef.Component1.MakeDouble() * InverseFraction; 01153 temp += EndDef.Component1.MakeDouble() * BlendFraction; 01154 SourceColour.CMYK.Cyan = (PColourValue) (temp + 0.5); 01155 01156 temp = StartDef.Component2.MakeDouble() * InverseFraction; 01157 temp += EndDef.Component2.MakeDouble() * BlendFraction; 01158 SourceColour.CMYK.Magenta = (PColourValue) (temp + 0.5); 01159 01160 temp = StartDef.Component3.MakeDouble() * InverseFraction; 01161 temp += EndDef.Component3.MakeDouble() * BlendFraction; 01162 SourceColour.CMYK.Yellow = (PColourValue) (temp + 0.5); 01163 01164 temp = StartDef.Component4.MakeDouble() * InverseFraction; 01165 temp += EndDef.Component4.MakeDouble() * BlendFraction; 01166 SourceColour.CMYK.Key = (PColourValue) (temp + 0.5); 01167 } 01168 else 01169 { 01170 // Pack the start colour straight into our SourceColour 01171 BlendContext->PackColour(&StartDef, &SourceColour); 01172 } 01173 } 01174 01175 01176 01177 /******************************************************************************************** 01178 01179 > void DocColour::MixHSV(DocColour *BlendStart, DocColour *BlendEnd, double BlendFraction, 01180 ColourContext *BlendContext, BOOL BlendTheLongWay) 01181 01182 Author: Jason_Williams (Xara Group Ltd) <camelotdev@xara.com> 01183 Created: 12/6/96 01184 01185 Inputs: BlendStart - The start DocColour 01186 BlendEnd - The end DocColour 01187 BlendFraction - Blending amount between 0.0 and 1.0 01188 BlendContext - the HSV colour context in which the blend will take place 01189 BlendTheLongWay - FALSE for rainbow, TRUE for Alt-Rainbow mixing 01190 01191 Purpose: Internal helper function for DocColour::Mix. 01192 This is a reasonably optimised routine for mixing HSV colours. 01193 01194 SeeAlso: DocColour::Mix 01195 01196 ********************************************************************************************/ 01197 01198 void DocColour::MixHSV(DocColour *BlendStart, DocColour *BlendEnd, double BlendFraction, 01199 ColourContext *BlendContext, BOOL BlendTheLongWay) 01200 { 01201 ERROR3IF(BlendStart == NULL || BlendEnd == NULL || BlendContext == NULL, "Illegal NULL params"); 01202 ERROR3IF(BlendContext->GetColourModel() != COLOURMODEL_HSVT, 01203 "DocColour::MixHSV is optimised for HSV colours only!"); 01204 01205 InitialiseInfoField(COLOURMODEL_HSVT); 01206 01207 ColourHSVT StartDef; 01208 if (BlendStart->IsTransparent()) // If no-colour then use white 01209 { 01210 BlendContext->GetWhite((ColourGeneric *) &StartDef); 01211 01212 if (BlendEnd->IsTransparent()) // If both ends no-colour, return no-colour 01213 Info.NoColour = TRUE; 01214 } 01215 else 01216 BlendContext->ConvertColour(BlendStart, (ColourGeneric *) &StartDef); 01217 01218 // If 0.0 then StartDef now has the colour to use (no blending necessary) 01219 // If BOTH colours are transparent, we will be NoColour, so no need to blend 01220 if (BlendFraction > 0.0 && !Info.NoColour) 01221 { 01222 ColourHSVT EndDef; 01223 if (BlendEnd->IsTransparent()) // If no-colour then use white 01224 BlendContext->GetWhite((ColourGeneric *) &EndDef); 01225 else 01226 BlendContext->ConvertColour(BlendEnd, (ColourGeneric *) &EndDef); 01227 01228 if (BlendTheLongWay) 01229 { 01230 // In Alt-rainbow blend, if exaclty one of the colours has undefined hue (it has 0 saturation 01231 // i.e. is black./grey/white), then Gavin decides to go to the Hue of the other colour 01232 // (i.e. do a full rainbow) rather than going to hue 0. 01233 if (StartDef.Saturation.MakeDouble() < 0.01 && EndDef.Saturation.MakeDouble() >= 0.01) 01234 { 01235 // Start has no hue, but End does, so copy Hue from End 01236 StartDef.Hue = EndDef.Hue; 01237 } 01238 else if (StartDef.Saturation.MakeDouble() >= 0.01 && EndDef.Saturation.MakeDouble() < 0.01) 01239 { 01240 // End has no hue, but Start does, so copy Hue from Start 01241 EndDef.Hue = StartDef.Hue; 01242 } 01243 } 01244 01245 const double InverseFraction = 1.0 - BlendFraction; 01246 01247 // HSV blend! We can go 2 ways, as HSV can 'wrap' from 1.0 back to 0.0 01248 BOOL BlendNormally = TRUE; 01249 01250 // Calc. the "simple" (non-wrapping) distance between the hues 01251 double StartHue = StartDef.Hue.MakeDouble(); 01252 double EndHue = EndDef.Hue.MakeDouble(); 01253 01254 const double SimpleDist = fabs(StartHue - EndHue); 01255 01256 // Determine whether we do a simple blend, or we have to "wrap" 01257 if (SimpleDist <= 0.5) 01258 BlendNormally = !(BlendTheLongWay); 01259 else 01260 BlendNormally = BlendTheLongWay; 01261 01262 // If we have to go the long way, then move the smaller of the two hue 01263 // values up by 360 degrees (1.0) - after blending we'll 'mod' the result 01264 // back down into gamut. 01265 if (!BlendNormally) 01266 { 01267 if (StartHue > EndHue) 01268 EndHue += 1.0; 01269 else 01270 StartHue += 1.0; 01271 } 01272 01273 // Do the blend 01274 double temp; 01275 temp = StartHue * InverseFraction; 01276 temp += EndHue * BlendFraction; 01277 01278 // And if we had to 'wrap', we must 'mod' the value back down into gamut 01279 if (temp > 1.0) 01280 temp -= 1.0; 01281 01282 StartDef.Hue = temp; 01283 01284 temp = StartDef.Saturation.MakeDouble() * InverseFraction; 01285 temp += EndDef.Saturation.MakeDouble() * BlendFraction; 01286 StartDef.Saturation = temp; 01287 01288 temp = StartDef.Value.MakeDouble() * InverseFraction; 01289 temp += EndDef.Value.MakeDouble() * BlendFraction; 01290 StartDef.Value = temp; 01291 } 01292 01293 // Pack the colour straight into our SourceColour 01294 BlendContext->PackColour((ColourGeneric *) &StartDef, &SourceColour); 01295 } 01296 01297 01298 01299 /******************************************************************************************** 01300 01301 > void DocColour::MixTint(IndexedColour *SpotParent, IndexedColour *StartTint, 01302 IndexedColour *EndTint, double BlendFraction) 01303 01304 Author: Jason_Williams (Xara Group Ltd) <camelotdev@xara.com> 01305 Created: 20/6/96 01306 01307 Inputs: SpotParent - The IndexedColour which both tints share in common. This 01308 MUST be the ultimate spot colour, not a tint half way down 01309 the linked chain. 01310 01311 StartTint - The tint colour for the start of the blend. Must be a true 01312 tint of SpotParent 01313 EndTint - The tint colour for the end of the blend. Must be a true 01314 tint of SpotParent 01315 01316 BlendFraction - Blending amount between 0.0 and 1.0 01317 01318 Purpose: Internal helper function for DocColour::Mix. 01319 This method is used to mix the DocColours to an IndexedColour result when 01320 one or both of the end colours are tints. This simply linearly 01321 interpolates the tint values. 01322 01323 SeeAlso: DocColour::Mix 01324 01325 ********************************************************************************************/ 01326 01327 void DocColour::MixTint(IndexedColour *SpotParent, IndexedColour *StartTint, 01328 IndexedColour *EndTint, double BlendFraction) 01329 { 01330 ERROR3IF(SpotParent == NULL || StartTint == NULL || EndTint == NULL, 01331 "Illegal NULL params"); 01332 01333 ERROR3IF(SpotParent->GetType() != COLOURTYPE_SPOT, "SpotParent must be a spot colour!"); 01334 01335 ERROR3IF(!StartTint->IsSpotOrTintOfSpot(), "StartTint must be a true tint!"); 01336 ERROR3IF(!EndTint->IsSpotOrTintOfSpot(), "EndTint must be a true tint!"); 01337 01338 ColourList *ColList = ColourManager::GetCurrentColourList(); 01339 if (ColList == NULL) 01340 { 01341 ERROR3("No current colour list?!"); 01342 return; 01343 } 01344 01345 IndexedColour *MixedLocal = new IndexedColour; 01346 if (MixedLocal == NULL) 01347 { 01348 ERROR3("Couldn't create IndexedColour local mix"); 01349 return; 01350 } 01351 01352 // Work out the start and end tint values _for use with the common ancestor_. 01353 double StartValue = StartTint->GetAccumulatedTintValue().MakeDouble(); 01354 double EndValue = EndTint->GetAccumulatedTintValue().MakeDouble(); 01355 01356 // Linearly interpolate the tint values 01357 double NewTint = (StartValue * (1.0 - BlendFraction)) + (EndValue * (BlendFraction)); 01358 01359 // Fill in the new local colour appropriately 01360 MixedLocal->SetLinkedParent(SpotParent, COLOURTYPE_TINT); 01361 MixedLocal->SetTintValue(NewTint); 01362 MixedLocal->SetUnnamed(); // Ensure it's unnamed (local) 01363 01364 // Add the local colour to the unnamed colour list 01365 ColList->AddItem(MixedLocal); 01366 01367 // And make this DocColour reference the new local 01368 MakeRefToIndexedColour(MixedLocal); 01369 } 01370 01371 /******************************************************************************************** 01372 01373 > DocColour::DocColour& operator=(const DocColour& Other) 01374 01375 Author: Jason_Williams (Xara Group Ltd) <camelotdev@xara.com> 01376 Created: 14/04/94 01377 Inputs: Other - colour to copy 01378 Outputs: - 01379 Returns: 01380 Purpose: DocColour assignment operator 01381 Notes: If copying a reference to an indexed colour, the indexed colours 01382 usage count will be incremented. 01383 If this is already a reference to an indexed colour, that indexed 01384 colours usage count will be decremented. 01385 01386 NOTE - SUPER IMPORTANT! 01387 This requires that the DocColour has been initialised by its 01388 constructor! You cannot thus treat an area of uninitialised memory 01389 as a DocColour and then try to initialise it using the copy constructor, 01390 as this could cause a fatal attempt to dereference a pointer. 01391 (This will hopefully be trapped by ENSURES) 01392 01393 If you need to initialise such a chunk of memory as a DocColour, 01394 then you'll need to call the Constructor in a special way. Ask Tim 01395 for details of how to do this. 01396 Errors: - 01397 01398 ********************************************************************************************/ 01399 01400 DocColour& DocColour::operator=(const DocColour& Other) 01401 { 01402 ENSURE(Info.SourceColourModel < MAX_COLOURMODELS, 01403 "Attempt to use illegal colour model!"); 01404 01405 if (Info.SourceColourModel == COLOURMODEL_INDEXED) 01406 { 01407 ENSURE(SourceColour.Indexed.Colour != NULL, 01408 "Corrupted DocColour (NULL IndexedColour ref) detected"); 01409 01410 CC_ASSERT_VALID(SourceColour.Indexed.Colour); 01411 01412 SourceColour.Indexed.Colour->DecrementUsage(); 01413 } 01414 01415 Info = Other.Info; 01416 CachedColour = Other.CachedColour; 01417 SourceColour = Other.SourceColour; 01418 01419 if (Info.SourceColourModel == COLOURMODEL_INDEXED) 01420 { 01421 ENSURE(SourceColour.Indexed.Colour != NULL, 01422 "Corrupted DocColour (NULL IndexedColour ref) detected"); 01423 01424 SourceColour.Indexed.Colour->IncrementUsage(); 01425 ENSURE(Info.CacheColourModel == 0, "RefToIndexedColour DocColour has a cache!!"); 01426 01427 #ifdef TRACECOLOURIX 01428 TRACE( _T(">> DocColour @ %x = (DocColour => ColourIx %x)\n"), (INT32)this, (INT32)SourceColour.Indexed.Colour); 01429 #endif 01430 } 01431 01432 return(*this); 01433 } 01434 01435 /******************************************************************************************** 01436 01437 > void DocColour::MakeRefToIndexedColour(IndexedColour *Other) 01438 01439 Author: Jason_Williams (Xara Group Ltd) <camelotdev@xara.com> 01440 Created: 03/05/94 01441 Inputs: Other - Indexed colour to assign 01442 Outputs: - 01443 Returns: 01444 Purpose: DocColour assignment - Makes this DocColour a reference to the 01445 given Indexed Colour. The Indexed Colour cannot be deleted until 01446 this DocColour releases this claim upon it (which occurs when it is 01447 deleted or re-assigned) 01448 01449 Notes: This is not done as an "operator=" because it does NOT make an 01450 independent copy of the other colour, merely a reference to it; 01451 I force you to use this method in order to remind you of this. 01452 This makes the given DocColour a reference to an Indexed Colour, 01453 incrementing the usage count of that indexed colour. 01454 If this is already a reference to an indexed colour, that indexed 01455 colours usage count will be decremented. 01456 01457 Errors: - 01458 SeeAlso: DocColour:FindParentIndexedColour 01459 01460 ********************************************************************************************/ 01461 01462 // --------- 01463 // Special debugging code to help track down IndexedColour usage 'leaks' 01464 // Set 'TRACECOLOURIX' at the top of this file if you want tracking information 01465 // spewing out into your trace stream. 01466 void DocColour::MakeRefToIndexedColourDBG(IndexedColour *Other, LPCSTR TheFile, INT32 TheLine) 01467 { 01468 #ifdef TRACECOLOURIX 01469 TRACE( _T(">> DocColour @ %x ref ColourIx: %x @ %s line %ld\n"), (INT32)this, (INT32)Other, TheFile, TheLine); 01470 #endif 01471 01472 // Disable the debugging macro so that the function code will compile 01473 #undef MakeRefToIndexedColour 01474 // --------- 01475 01476 MakeRefToIndexedColour(Other); 01477 } 01478 01479 01480 01481 void DocColour::MakeRefToIndexedColour(IndexedColour *Other) 01482 { 01483 ENSURE(Other != NULL, 01484 "NULL IndexedColour pointer passed to DocColour::MakeRefToIndexedColour!"); 01485 01486 if (Info.SourceColourModel == COLOURMODEL_INDEXED) 01487 { 01488 ENSURE(SourceColour.Indexed.Colour != NULL, 01489 "Corrupted DocColour (NULL IndexedColour ref) detected"); 01490 SourceColour.Indexed.Colour->DecrementUsage(); 01491 } 01492 01493 Info.NoColour = FALSE; 01494 Info.SourceColourModel = COLOURMODEL_INDEXED; 01495 Info.CacheColourModel = COLOURMODEL_NOTCACHED; // NEVER cache indexed colours 01496 Info.OCContextHandle = 0; 01497 Info.IsSeparable = TRUE; // IxCols are always separable 01498 01499 SourceColour.Indexed.Colour = Other; 01500 Other->IncrementUsage(); 01501 } 01502 01503 01504 01505 /******************************************************************************************** 01506 01507 > IndexedColour *DocColour::GetSpotParent(void) 01508 01509 Author: Jason_Williams (Xara Group Ltd) <camelotdev@xara.com> 01510 Created: 25/6/96 01511 01512 Returns: NULL if this DocColour does not reference an indexed colour, or if the 01513 referenced colour is not a spot colour, else 01514 A pointer to the ultimate parent spot colour of this colour. 01515 01516 Purpose: To determine which spot colour (if any) is the ultimate spot colour 01517 parent of this DocColour. (i.e. to see which spot plate a DocColour 01518 will appear on). 01519 01520 Notes: This only returns a non-NULL result if this colour is a TRUE tint of 01521 a spot colour (as opposed to a tint of a process colour). 01522 01523 ********************************************************************************************/ 01524 01525 IndexedColour *DocColour::GetSpotParent(void) 01526 { 01527 IndexedColour *Parent = FindParentIndexedColour(); 01528 if (Parent != NULL) 01529 { 01530 if (!Parent->IsSpotOrTintOfSpot()) // If we aren't a TRUE tint, return NULL 01531 return(NULL); 01532 01533 Parent = Parent->FindOldestAncestor(); // ...else find our spot parent 01534 if (Parent->IsSpotOrTintOfSpot()) // (and check just to be really safe) 01535 return(Parent); 01536 } 01537 01538 // no parent, or it's not a spot colour 01539 return(NULL); 01540 } 01541 01542 /******************************************************************************************** 01543 01544 > BOOL DocColour::operator==(const DocColour Other) const 01545 01546 Author: Jason_Williams (Xara Group Ltd) <camelotdev@xara.com> 01547 Created: 14/04/94 01548 Inputs: Other - colour to compare ourselves against 01549 Outputs: - 01550 Returns: TRUE if we are 'identical' to the other colour. 01551 01552 Purpose: DocColour comparison operator 01553 01554 Notes: When comparing 2 colours, they will be considered equal if: 01555 * They are both 'no colour' (fully transparent), or 01556 * They are *exactly* equal, i.e. the same colour defined in the same 01557 colour model (Pure RGB red is NOT equal to pure HSV red) 01558 01559 Note that for this comparison, references to indexedcolours 01560 are compared as references, i.e. it does NOT compare the definitions 01561 of IndexedColours, only the pointer to the IndexedColours (so 01562 IndexedColour references are essentially treated as another colour model) 01563 01564 Errors: - 01565 SeeAlso: DocColour::GetColourModel 01566 01567 ********************************************************************************************/ 01568 01569 BOOL DocColour::operator==(const DocColour Other) const 01570 { 01571 if (Info.NoColour && Other.Info.NoColour) 01572 return(TRUE); 01573 01574 return (Info.NoColour == Other.Info.NoColour && 01575 Info.SourceColourModel == Other.Info.SourceColourModel && 01576 memcmp(&SourceColour, &Other.SourceColour, sizeof(ColourPacked)) == 0 ); 01577 } 01578 01579 01580 01581 /******************************************************************************************** 01582 01583 > BOOL DocColour::operator!=(const DocColour Other) const 01584 01585 Author: Jason_Williams (Xara Group Ltd) <camelotdev@xara.com> 01586 Created: 14/04/94 01587 Inputs: Other - colour to compare ourselves against 01588 Outputs: - 01589 Returns: FALSE if we are identical to the other colour. NOTE that this 01590 will only be the case if we are both defined in the same colour 01591 model (i.e. pure red defined in HSVT is NOT equal to pure red in RGBT) 01592 Purpose: DocColour comparison operator 01593 Notes: If comparing two references to indexed colours, they will only 01594 be considered equal if they reference the SAME indexed colour 01595 (i.e. we do not compare the indexed colours) 01596 Errors: - 01597 SeeAlso: DocColour::GetColourModel 01598 01599 ********************************************************************************************/ 01600 01601 BOOL DocColour::operator!=(const DocColour Other) const 01602 { 01603 return (Info.NoColour != Other.Info.NoColour || 01604 Info.SourceColourModel != Other.Info.SourceColourModel || 01605 memcmp(&SourceColour, &Other.SourceColour, sizeof(ColourPacked)) != 0 ); 01606 } 01607 01608 // --------------------------------------------------------------------------------------- 01609 // Functions below this point have been left in purely for backward compatability with the 01610 // old colour system. They may be removed in the future if they are found to be unused. 01611 01612 DocColour::DocColour(INT32 Red, INT32 Green, INT32 Blue) 01613 { 01614 ZapSourceColour(); 01615 InitialiseInfoField(COLOURMODEL_RGBT); 01616 01617 SourceColour.RGBT.Red = (PColourValue) Red; 01618 SourceColour.RGBT.Green = (PColourValue) Green; 01619 SourceColour.RGBT.Blue = (PColourValue) Blue; 01620 SourceColour.RGBT.Transparent = 0; 01621 } 01622 01623 01624 01625 void DocColour::SetRGBValue(INT32 Red, INT32 Green, INT32 Blue) 01626 // NOTE that this forces our colourmodel to RGBT. 01627 { 01628 if (Info.SourceColourModel == COLOURMODEL_INDEXED) 01629 { 01630 ENSURE(SourceColour.Indexed.Colour != NULL, 01631 "Corrupted DocColour (NULL IndexedColour ref) detected"); 01632 SourceColour.Indexed.Colour->DecrementUsage(); 01633 } 01634 01635 InitialiseInfoField(COLOURMODEL_RGBT); 01636 01637 SourceColour.RGBT.Red = (PColourValue) Red; 01638 SourceColour.RGBT.Green = (PColourValue) Green; 01639 SourceColour.RGBT.Blue = (PColourValue) Blue; 01640 SourceColour.RGBT.Transparent = 0; 01641 } 01642 01643 01644 void DocColour::GetRGBValue(INT32* Red, INT32* Green, INT32* Blue) 01645 { 01646 ColourContextRGBT *CCrgbt = (ColourContextRGBT *)ColourContext::GetGlobalDefault(COLOURMODEL_RGBT); 01647 ColourRGBT result; 01648 ColourPacked bob; 01649 01650 CCrgbt->ConvertColour((DocColour *)this, (ColourGeneric *)&result); 01651 CCrgbt->PackColour((ColourGeneric *)&result, (ColourPacked *)&bob); 01652 01653 *Red = (INT32) bob.RGBT.Red; 01654 *Green = (INT32) bob.RGBT.Green; 01655 *Blue = (INT32) bob.RGBT.Blue; 01656 } 01657 01658 01659 void DocColour::SetHSVValue(INT32 h, INT32 s, INT32 v) 01660 // NOTE that this forces our colourmodel to HSVT. 01661 { 01662 if (Info.SourceColourModel == COLOURMODEL_INDEXED) 01663 { 01664 ENSURE(SourceColour.Indexed.Colour != NULL, 01665 "Corrupted DocColour (NULL IndexedColour ref) detected"); 01666 SourceColour.Indexed.Colour->DecrementUsage(); 01667 } 01668 01669 InitialiseInfoField(COLOURMODEL_HSVT); 01670 01671 SourceColour.HSVT.Hue = (PColourValue) h; 01672 SourceColour.HSVT.Saturation = (PColourValue) s; 01673 SourceColour.HSVT.Value = (PColourValue) v; 01674 SourceColour.HSVT.Transparent = 0; 01675 } 01676 01677 01678 void DocColour::GetHSVValue(INT32* h, INT32* s, INT32* v) 01679 { 01680 ColourContextHSVT *CChsvt = (ColourContextHSVT *)ColourContext::GetGlobalDefault(COLOURMODEL_HSVT); 01681 ColourHSVT result; 01682 ColourPacked bob; 01683 01684 CChsvt->ConvertColour((DocColour *)this, (ColourGeneric *)&result); 01685 CChsvt->PackColour((ColourGeneric *)&result, (ColourPacked *)&bob); 01686 01687 *h = (INT32) bob.HSVT.Hue; 01688 *s = (INT32) bob.HSVT.Saturation; 01689 *v = (INT32) bob.HSVT.Value; 01690 } 01691 01692 01693 void DocColour::SetCMYKValue(PColourCMYK *New) 01694 { 01695 if (Info.SourceColourModel == COLOURMODEL_INDEXED) 01696 { 01697 ENSURE(SourceColour.Indexed.Colour != NULL, 01698 "Corrupted DocColour (NULL IndexedColour ref) detected"); 01699 SourceColour.Indexed.Colour->DecrementUsage(); 01700 } 01701 01702 InitialiseInfoField(COLOURMODEL_CMYK); 01703 01704 memcpy(&SourceColour, New, sizeof(PColourCMYK)); 01705 } 01706 01707 01708 void DocColour::GetCMYKValue(PColourCMYK *Result) 01709 { 01710 ColourContext *CCcmyk = ColourContext::GetGlobalDefault(COLOURMODEL_CMYK); 01711 CCcmyk->ConvertColour(this, (ColourPacked *) Result); 01712 } 01713 01714 01715 void DocColour::GetCMYKValue(ColourContext* pContext, PColourCMYK *Result) 01716 { 01717 if (pContext!=NULL) 01718 { 01719 ERROR3IF(pContext->GetColourModel() != COLOURMODEL_CMYK, "Colour context is not CMYK!"); 01720 pContext->ConvertColour(this, (ColourPacked *) Result); 01721 return; 01722 } 01723 01724 ColourContext *CCcmyk = ColourContext::GetGlobalDefault(COLOURMODEL_CMYK); 01725 ERROR3IF(CCcmyk == NULL, "No default CMYK colour context?!"); 01726 01727 CCcmyk->ConvertColour(this, (ColourPacked *) Result); 01728 } 01729 01730 01731 01732 void DocColour::GetDebugDetails(StringBase* Str) 01733 { 01734 String_256 TempStr; 01735 01736 if (Info.IsSeparable) 01737 *Str += TEXT(" Sep "); 01738 else 01739 *Str += TEXT(" NotSep "); 01740 01741 if (Info.SourceColourModel == COLOURMODEL_INDEXED) 01742 { 01743 ENSURE(SourceColour.Indexed.Colour != NULL, 01744 "Corrupted DocColour (NULL IndexedColour ref) detected"); 01745 01746 String_64 *pIndexedName = SourceColour.Indexed.Colour->GetName(); 01747 // TempStr._MakeMsg(TEXT(" (#1%s) ref->"), 01748 // pIndexedName); // This is a reference to an indexed col 01749 // (*Str) += TempStr; 01750 (*Str) += TEXT(" ("); 01751 (*Str) += (*pIndexedName); 01752 (*Str) += TEXT(") ref ->"); 01753 SourceColour.Indexed.Colour->GetDebugDetails(Str); 01754 return; 01755 } 01756 01757 01758 if (Info.NoColour) 01759 TempStr._MakeMsg( TEXT(" colour=transparent\r\n")); 01760 else 01761 { 01762 ColourContext *cc = ColourContext::GetGlobalDefault((ColourModel) Info.SourceColourModel); 01763 ColourGeneric col; 01764 01765 String_32 ModelName; 01766 cc->GetModelName(&ModelName); 01767 cc->UnpackColour(&SourceColour, &col); 01768 01769 TempStr._MakeMsg( TEXT(" colour=#1%s(#2%ld, #3%ld, #4%ld, #5%ld)\r\n"), (TCHAR *) ModelName, 01770 (INT32) (col.Component1.MakeDouble()*100), (INT32) (col.Component2.MakeDouble()*100), 01771 (INT32) (col.Component3.MakeDouble()*100), (INT32) (col.Component4.MakeDouble()*100)); 01772 } 01773 01774 (*Str) += TempStr; 01775 }