ai_grad.cpp

Go to the documentation of this file.
00001 // $Id: ai_grad.cpp 1313 2006-06-13 16:55:22Z alex $
00002 // ai_grad.cpp: implementation of the AIGradientProcessor class.
00003 //
00005 /* @@tag:xara-cn@@ DO NOT MODIFY THIS LINE
00006 ================================XARAHEADERSTART===========================
00007  
00008                Xara LX, a vector drawing and manipulation program.
00009                     Copyright (C) 1993-2006 Xara Group Ltd.
00010        Copyright on certain contributions may be held in joint with their
00011               respective authors. See AUTHORS file for details.
00012 
00013 LICENSE TO USE AND MODIFY SOFTWARE
00014 ----------------------------------
00015 
00016 This file is part of Xara LX.
00017 
00018 Xara LX is free software; you can redistribute it and/or modify it
00019 under the terms of the GNU General Public License version 2 as published
00020 by the Free Software Foundation.
00021 
00022 Xara LX and its component source files are distributed in the hope
00023 that it will be useful, but WITHOUT ANY WARRANTY; without even the
00024 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00025 See the GNU General Public License for more details.
00026 
00027 You should have received a copy of the GNU General Public License along
00028 with Xara LX (see the file GPL in the root directory of the
00029 distribution); if not, write to the Free Software Foundation, Inc., 51
00030 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
00031 
00032 
00033 ADDITIONAL RIGHTS
00034 -----------------
00035 
00036 Conditional upon your continuing compliance with the GNU General Public
00037 License described above, Xara Group Ltd grants to you certain additional
00038 rights. 
00039 
00040 The additional rights are to use, modify, and distribute the software
00041 together with the wxWidgets library, the wxXtra library, and the "CDraw"
00042 library and any other such library that any version of Xara LX relased
00043 by Xara Group Ltd requires in order to compile and execute, including
00044 the static linking of that library to XaraLX. In the case of the
00045 "CDraw" library, you may satisfy obligation under the GNU General Public
00046 License to provide source code by providing a binary copy of the library
00047 concerned and a copy of the license accompanying it.
00048 
00049 Nothing in this section restricts any of the rights you have under
00050 the GNU General Public License.
00051 
00052 
00053 SCOPE OF LICENSE
00054 ----------------
00055 
00056 This license applies to this program (XaraLX) and its constituent source
00057 files only, and does not necessarily apply to other Xara products which may
00058 in part share the same code base, and are subject to their own licensing
00059 terms.
00060 
00061 This license does not apply to files in the wxXtra directory, which
00062 are built into a separate library, and are subject to the wxWindows
00063 license contained within that directory in the file "WXXTRA-LICENSE".
00064 
00065 This license does not apply to the binary libraries (if any) within
00066 the "libs" directory, which are subject to a separate license contained
00067 within that directory in the file "LIBS-LICENSE".
00068 
00069 
00070 ARRANGEMENTS FOR CONTRIBUTION OF MODIFICATIONS
00071 ----------------------------------------------
00072 
00073 Subject to the terms of the GNU Public License (see above), you are
00074 free to do whatever you like with your modifications. However, you may
00075 (at your option) wish contribute them to Xara's source tree. You can
00076 find details of how to do this at:
00077   http://www.xaraxtreme.org/developers/
00078 
00079 Prior to contributing your modifications, you will need to complete our
00080 contributor agreement. This can be found at:
00081   http://www.xaraxtreme.org/developers/contribute/
00082 
00083 Please note that Xara will not accept modifications which modify any of
00084 the text between the start and end of this header (marked
00085 XARAHEADERSTART and XARAHEADEREND).
00086 
00087 
00088 MARKS
00089 -----
00090 
00091 Xara, Xara LX, Xara X, Xara X/Xtreme, Xara Xtreme, the Xtreme and Xara
00092 designs are registered or unregistered trademarks, design-marks, and/or
00093 service marks of Xara Group Ltd. All rights in these marks are reserved.
00094 
00095 
00096       Xara Group Ltd, Gaddesden Place, Hemel Hempstead, HP2 6EX, UK.
00097                         http://www.xara.com/
00098 
00099 =================================XARAHEADEREND============================
00100  */
00101 
00102 #include "camtypes.h"
00103 #include "ai_grad.h"
00104 
00105 #include "ai5_eps.h"
00106 #include "fillramp.h"
00107 
00108 DECLARE_SOURCE("$Revision");
00109 
00110 #define new CAM_DEBUG_NEW
00111 
00112 /********************************************************************************************
00113 
00114 >   AI5Gradient::AI5Gradient()
00115 
00116     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00117     Created:    12/08/94
00118     Purpose:    Set up a gradient record to sensible values (linear fill black to black).
00119     SeeAlso:    AI5Gradient; AI5EPSFilter
00120 
00121 ********************************************************************************************/
00122 AI5Gradient::AI5Gradient() :
00123     IsRadial( FALSE ),
00124     StartColour( BLACK ),
00125     EndColour( BLACK ),
00126     mpCurrentRamp( 0 )
00127 {
00128 }
00129 
00130 
00131 AI5Gradient::~AI5Gradient()
00132 {
00133     delete mpCurrentRamp;
00134     mpCurrentRamp = 0;
00135 }
00136 
00139 
00140 
00141 
00142 
00143 
00145 // Construction/Destruction
00147 
00148 AIGradientProcessor::AIGradientProcessor() :
00149     mbGradFillTooComplex(FALSE),
00150     mpCurrentFill(0)
00151 {
00152     // The last ramp point is set to 101, since this is effectively a
00153     // counter from 100 down to 0.
00154     mMidPoint       = 0;
00155     mLastRampPoint  = 101;
00156 }
00157 
00158 AIGradientProcessor::~AIGradientProcessor()
00159 {
00160     mGradFills.DeleteAll();
00161 
00162     delete mpCurrentFill;
00163     mpCurrentFill = 0;
00164 }
00165 
00166 
00167 /********************************************************************************************
00168 
00169 >   BOOL AIGradientProcessor::BeginGradient()
00170 
00171     Author:     Colin_Barfoot (Xara Group Ltd) <camelotdev@xara.com>
00172     Created:    23/03/00
00173 
00174     Purpose:    Allows the gradient processor to intialize itself for a new gradient
00175                 signalled by the %BeginGradient comment.
00176 
00177     Returns:    TRUE if the definition was processed, 
00178                 FALSE if not used by this filter..
00179 
00180 ********************************************************************************************/
00181 BOOL AIGradientProcessor::BeginGradient()
00182 {
00183     ENSURE( mpCurrentFill == NULL, "mpCurrentFill is not NULL");
00184 
00185     // Found the start of a gradient definition - prepare to read in the
00186     // definition...
00187     mpCurrentFill = new AI5Gradient;
00188 
00189     if ( !mpCurrentFill )
00190         return FALSE;
00191 
00192     return TRUE;
00193 }
00194 
00195 
00196 
00197 /********************************************************************************************
00198 
00199 >   BOOL AIGradientProcessor::DecodeBd( AI5EPSFilter& filter )
00200 
00201     Author:     Colin_Barfoot (Xara Group Ltd) <camelotdev@xara.com>
00202     Created:    23/03/00
00203 
00204     Purpose:    Processes the gradient definition start operator which tells us how many
00205                 colours are in it and what its name is.
00206 
00207     Returns:    TRUE if the definition was processed, 
00208                 FALSE if not used by this filter..
00209 
00210 ********************************************************************************************/
00211 BOOL AIGradientProcessor::DecodeBd( AI5EPSFilter& filter )
00212 {
00213     if ( !mpCurrentFill )
00214         return FALSE;
00215 
00217     // Name, type, and number of steps in gradient fill
00219     INT32 colours = 0;
00220     INT32 type = 0;
00221 
00222     if (!filter.GetStack().Pop(&colours) || 
00223         !filter.GetStack().Pop(&type) || 
00224         !filter.GetStack().Pop(&mpCurrentFill->Name))
00225         // Error in syntax
00226         return FALSE;
00227 
00228     // Translate fill type
00229     switch (type)
00230     {
00231         case 0:     mpCurrentFill->IsRadial = FALSE;    break;
00232         case 1:     mpCurrentFill->IsRadial = TRUE;     break;
00233         default:    return FALSE;
00234     }
00235 
00236     return TRUE;
00237 }
00238 
00239 /********************************************************************************************
00240 
00241 >   BOOL AIGradientProcessor::DecodeBS( AI5EPSFilter& filter )
00242 
00243     Author:     Colin_Barfoot (Xara Group Ltd) <camelotdev@xara.com>
00244     Created:    23/03/00
00245 
00246     Purpose:    Processes the gradient stop operator (indicating a step in the ramp)
00247 
00248     Returns:    TRUE if the definition was processed, 
00249                 FALSE if not used by this filter..
00250 
00251 ********************************************************************************************/
00252 BOOL AIGradientProcessor::DecodeBS( AI5EPSFilter& filter )
00253 {
00254     // Decode colour/step definition
00255     INT32 rampPoint = 0, midPoint = 0;
00256 
00257     if ( !filter.GetStack().Pop(&rampPoint) || !filter.GetStack().Pop(&midPoint) )
00258         return FALSE;
00259 
00260     // Check if these are values we can handle
00261     // (We still import an approximation of the fill even if not)
00262     if ( midPoint > 100 || midPoint < 0 || rampPoint < 0 || rampPoint > 100 )
00263     {
00264         return FALSE;
00265     }
00266 
00267     // Work out what sort of colour this is - CMYK or B&W tint
00268     INT32 colorStyle;
00269     if ( !filter.GetStack().Pop(&colorStyle) )
00270         return FALSE;
00271 
00272     DocColour newColour;
00273 
00274     switch ( colorStyle )
00275     {
00276         case 0:     // grey
00277         {
00278             double dTint( 0.0 );
00279             if ( !filter.GetStack().Pop(&dTint) )
00280                 return FALSE;
00281 
00282             // Make a colour from this
00283             INT32 intensity = (INT32) (dTint * 255.0);
00284             newColour.SetRGBValue( intensity, intensity, intensity );
00285             break;
00286         }
00287 
00288         case 1:     // CMYK
00289         {
00290             PColourCMYK col;
00291             if (!filter.GetStack().PopColour(&col, TINT_NONE))
00292                 return FALSE;
00293 
00294             // Set up the colour with what we have.
00295             newColour.SetCMYKValue( &col );
00296 
00297             break;
00298         }
00299 
00300         case 2:     // RGB
00301         {
00302             INT32 red, green, blue;
00303             if (!filter.GetStack().PopColourRGB (&red, &green, &blue, TINT_NONE))
00304                 return FALSE;
00305 
00306                 // remove the CMYK colour info.
00307             (filter.GetStack()).Discard (4);
00308             newColour.SetRGBValue (red, green, blue);
00309             break;
00310         }
00311 
00312         case 3:     // CMYK custom
00313         {
00314             PColourCMYK col;
00315             FIXEDPOINT tint;
00316             String_64 name;
00317             if ( !filter.GetStack().PopColour( &col, TINT_ILLUSTRATOR, &tint, &name ) )
00318                 return FALSE;
00319 
00320             newColour.SetCMYKValue( &col );
00321 
00322             break;
00323         }
00324 
00325         case 4:     // RGB custom
00326         {
00327             INT32 red, green, blue;
00328             FIXEDPOINT tint;
00329             String_64 name;
00330 
00331             // remove the 'type' flag (is always 1 for RGB)
00332             filter.GetStack().Discard (1);
00333 
00334             if (!filter.GetStack().PopColourRGB (&red, &green, &blue, 
00335                                                 TINT_ILLUSTRATOR, &tint, &name))
00336                 return FALSE;
00337 
00338             (filter.GetStack()).Discard (4);
00339             newColour.SetRGBValue (red, green, blue);
00340 
00341             break;
00342         }
00343         default:
00344             break;
00345     }
00346 
00347 
00349     //  add the colour to the ramp
00351 
00352     double dPos = double(rampPoint) / double(100.0);
00353 
00354     if ( mpCurrentFill )
00355     {
00356         if ( !mpCurrentFill->mpCurrentRamp )
00357         {
00358             mpCurrentFill->mpCurrentRamp = new ColourRamp;
00359             if ( ! mpCurrentFill->mpCurrentRamp )
00360             {
00361                 return FALSE;
00362             }
00363         }
00364 
00365         mpCurrentFill->mpCurrentRamp->AddEntry( static_cast<float> ( dPos ), &newColour );
00366     }
00367 
00368     // Set the midpoint if this point is nearer to the start than the stored mid-point. 
00369     //  The midpoint nearest to the 0% ramp point is used, as there should only be one 
00370     //  midpoint if Xara X's profiling is going to cope with it, and the last midpoint in 
00371     //  an Illustrator gradient should always be ignored.
00372     if (rampPoint < mLastRampPoint)
00373     {
00374         mMidPoint = midPoint;
00375         mLastRampPoint = rampPoint;
00376     }
00377 
00378     return TRUE;
00379 }
00380 
00381 
00382 /********************************************************************************************
00383 
00384 >   BOOL AIGradientProcessor::DecodeBh( AI5EPSFilter& filter )
00385 
00386     Author:     Colin_Barfoot (Xara Group Ltd) <camelotdev@xara.com>
00387     Created:    23/03/00
00388 
00389     Purpose:    The Bh operator adds a highlight to a fill. In Camelot we interpret this
00390                 as a conical fill.
00391 
00392     Returns:    TRUE if the definition was processed happily
00393                 FALSE if not
00394 
00395 
00396     The format of the operator is:
00397         xHilight yHilight angle length Bh
00398 
00399     Arguments to the Bh operator are as follows:
00400         xHilight yHilight   These arguments specify the hilight placement, in x and y offsets 
00401                             from the gradient vector origin.
00402         angle               This argument is the angle to the hilight point, measured 
00403                             counterclockwise from the x axis.
00404         length              This argument is the distance of the hilight from the origin, 
00405                             expressed as a fraction of the radius a value between 0 and 1.
00406 
00407 
00408 ********************************************************************************************/
00409 BOOL AIGradientProcessor::DecodeBh( AI5EPSFilter& filter )
00410 {
00412     // read the arguments from the stack
00414 
00415     INT32 nAngle        = 0;
00416     double dLength  = 0.0;
00417     DocCoord hilight;
00418 
00419     if ( !filter.GetStack().Pop(&dLength) || !filter.GetStack().Pop(&nAngle) ||
00420         !filter.GetStack().PopCoordPair(&hilight) )
00421         return FALSE;
00422 
00424     // Check if these are values we can handle
00426 
00428     // Attach the highlight details so we can work out what to do later
00429     // but if there's already a highlight ignore this one
00431 /*
00432 
00433   Won't do this for now
00434 
00435 
00436     mpCurrentHighlight = new AI5Gradient::Highlight( nAngle, dLength, hilight );
00437 */
00438     return TRUE;
00439 }
00440 
00441 /********************************************************************************************
00442 
00443 >   BOOL AIGradientProcessor::Approx (const double & d1, const double & d2)
00444 
00445     Author:     ?
00446     Created:    presumably March 2000
00447     Inputs:     d1, d2 - two doubles that are to be compared
00448     Purpose:    This is used to determine whether the position of one gradient stop is 
00449                 equivalent to another during import, mainly for working out which stops
00450                 are at the start and the end.
00451 
00452                 Currently the tolerance for this is 2% either way, as this seems to give
00453                 good results.
00454 
00455     Returns:    TRUE if the d1 is approximately equal to d2
00456                 FALSE if not
00457 
00458 ********************************************************************************************/
00459 BOOL AIGradientProcessor::Approx (const double & d1, const double & d2)
00460 {
00461     const double dTolerance = 2;
00462     return ( fabs( d2 - d1 ) < dTolerance );
00463 }
00464 
00465 
00466 /********************************************************************************************
00467 
00468 >   BOOL AIGradientProcessor::EndGradient()
00469 
00470     Author:     Colin_Barfoot (Xara Group Ltd) <camelotdev@xara.com>
00471     Created:    23/03/00
00472 
00473     Purpose:    Allows the gradient processor to complete the gradient
00474                 signalled by the %EndGradient comment.
00475 
00476     Returns:    TRUE if the definition was processed, 
00477                 FALSE if not used by this filter..
00478 
00479 ********************************************************************************************/
00480 BOOL AIGradientProcessor::EndGradient( AI5EPSFilter& filter )
00481 {
00482     ENSURE( mpCurrentFill != NULL, "mpGradFill is NULL");
00483 
00484     // The end of the definition - lose the rampant '[' from the input stream.
00485     EPSCommand Cmd;
00486     if (!filter.GetStack().PopCmd(&Cmd) || (Cmd != EPSC_ArrayStart))
00487     {
00488         // if it isn't what we're hoping for, ignore it
00489         filter.GetStack().Push( Cmd );
00490     }
00491 
00493     // an n-stage multi-stage fill in camelot is divided into n - 2 ramp items
00494     // and the start and end colours of the fill.
00495     // When creating the fill we have just added ramp items, so we need to pick 
00496     // off the first and last for the start and end colours.
00498 
00499     if ( mpCurrentFill->mpCurrentRamp )
00500     {   
00501         // (ChrisG 3/4/2001) Gradient stops are now always removed from the list if they are
00502         //  used as a start or end colour. There was a check to make sure they were near the
00503         //  end, which often failed, leading to additional stops at the ends.
00504 
00505         // first the start colour
00506         ColRampItem* pFirstItem = reinterpret_cast<ColRampItem*>( mpCurrentFill->mpCurrentRamp->GetHead() );
00507         if ( pFirstItem )
00508         {
00509             // Take the first colour out of the ramp, as this is represented by the start colour
00510             mpCurrentFill->StartColour = pFirstItem->GetColour();
00511 
00512             // if the first ramp item isn't at the start of the fill, leave it as
00513             // part of the ramp and make the start the same colour
00514             if ( Approx( pFirstItem->GetPosition(), 0.0) ||
00515                  Approx( pFirstItem->GetPosition(), 100.0))
00516             {
00517                 mpCurrentFill->mpCurrentRamp->RemoveHead();
00518             }
00519         }
00520 
00521         // then the end colour
00522         ColRampItem* pLastItem = reinterpret_cast<ColRampItem*>( mpCurrentFill->mpCurrentRamp->GetTail() );
00523         if ( pLastItem )
00524         {
00525             // Take the last colour out of the ramp, as this is represented by the end colour
00526             mpCurrentFill->EndColour = pLastItem->GetColour();
00527 
00528             // if the first ramp item isn't at the start of the fill, leave it as
00529             // part of the ramp and make the start the same colour
00530             if ( Approx( pLastItem->GetPosition(), 0.0) ||
00531                  Approx( pLastItem->GetPosition(), 100.0))
00532             {
00533                 mpCurrentFill->mpCurrentRamp->RemoveTail();
00534             }
00535         }
00536     }
00537 
00538     // set the mid-point, for the profiling.
00539     mpCurrentFill->midPoint = mMidPoint;
00540 
00541     // add the current fill to our list.
00542     mGradFills.AddTail( mpCurrentFill );
00543 
00544     // We're done with this fill.
00545     mpCurrentFill = NULL;
00546 
00547     // reset the profiling variables.
00548     mMidPoint       = 0;
00549     mLastRampPoint  = 101;
00550 
00551     return TRUE;
00552 }
00553 
00554 
00555 /********************************************************************************************
00556 
00557 >   AI5Gradient* AIGradientProcessor::FindGradient( const StringBase& name )
00558 
00559     Author:     Colin_Barfoot (Xara Group Ltd) <camelotdev@xara.com>
00560     Created:    23/03/00
00561 
00562     Purpose:    Once the gradient definitions have been read, gradient instances can be
00563                 created using the name to identify the gradient "style".
00564 
00565     Returns:    NULL if no gradient exists with the given name
00566                 Otherwise the one that does.
00567 
00568 ********************************************************************************************/
00569 AI5Gradient* AIGradientProcessor::FindGradient( const StringBase& name )
00570 {
00571     // First find this named gradient fill
00572     AI5Gradient* pGradient = (AI5Gradient *) mGradFills.GetHead();
00573 
00574     while ( pGradient )
00575     {
00576         if ( pGradient->Name == name )
00577             // This is the one we want!
00578             break;
00579 
00580         // Try the next one
00581         pGradient = (AI5Gradient *) mGradFills.GetNext(pGradient);
00582     }
00583 
00584     return pGradient;
00585 }

Generated on Sat Nov 10 03:44:09 2007 for Camelot by  doxygen 1.4.4