TweetFollow Us on Twitter

SuperPaint Plug-in
Volume Number:8
Issue Number:1
Column Tag:Pascal Workshop

Related Info: Color Quickdraw Memory Manager

Superpaint Airbrush Masking

A plug-in for creating & using masks

By Allen Stenger, Gardena, California

Note: Source code files accompanying article are located on MacTech CD-ROM or source code disks.

Introduction

The airbrushes and spray cans included in paint programs tend to spatter areas next to the one you want to paint, making it difficult to paint neatly. Artists using real airbrushes have the same problem, but solve it with masks and shields. A mask is a piece of acetate or similar material, cut to cover the areas to be protected from paint, and pasted to the surface. The artist paints the exposed area with the airbrush, then peels away the mask. A shield is similar, but it is held in one hand (instead of being pasted down) and moved around while the other hand paints with the airbrush.

This article gives a plug-in program for creating and using masks in SuperPaint.

Taking Liberties

Our masks are implemented by the slightly devious method of saving the area under the mask, then restoring it later to “peel off” the mask. This is a kind of cut and paste, and perhaps cut and paste could be used sometimes, although it is hard to control exactly where the pasted object goes.

You define a mask using a selection tool. When SuperPaint calls the plug-in, it describes the selection as a BitMap where the bit image has 1’s in the selected pixels (like CalcMask’s output). We store this BitMap as the mask, offsetting its boundsRect so that the mask is in document coordinates. (Selections made with the selection rectangle are not described this way; we get the Rect and make our own BitMap.) You also can cut out parts of the mask; this is handled the same way, except that we use CopyBits with the srcBic transfer mode to remove the cut-out area from the saved BitMap. Capturing the image under the mask is also done with CopyBits; restoring it uses CopyMask to restore only those pixels under the mask.

One drawback of using selection tools to define the mask is that the mask is invisible! To get around this I added a “Tint Mask” command that inverts all pixels under the mask. By first capturing the area under the mask, you can then tint the mask to see where you’re painting; restoring under the mask obliterates the tint.

SuperPaint painting commands are described formally as “Black and White transformation commands,” and typically transform an area on a single call rather than building up a transform across several calls, as we do here. There’s nothing wrong with our approach, although the interface is clumsy since you must always select something (usually an area containing the mask) for each call.

Example

This is an exercise from Vero’s book. Start by making the mask, which is an outline of the drop. Draw a Bezier in the Draw layer (I start with an oval and bend it into shape). Then cut this into the Paint layer.

First draw the shadow. Select the mask with the lasso, and command Outline A Mask. Open a new window, command Capture Under Mask, then Tint Mask. (Note that the Masker does not remember which window it was in.) Use the airbrush set for 25% flow, 4 pixels and faded to paint the shadow with a 50% gray. Be sure the boundary is well-defined; keep spraying until it is thick enough. Use the Command Restore Under Mask to check your progress. Now mask the outside of the drop by going back to the mask window, selecting the window (double-click on the selection rectangle), commanding Outline A Mask, then selecting the outline with the lasso and commanding Remove From Mask. Go back to the drop window, select the entire window, and command Capture Under Mask. Fill the window with 12.5% gray, then Restore Under Mask to get the drop. Paint the highlight with white, using a horizontal blade brush (1 x 8 pixels). Lighten the bottom edge of the drop with the airbrush set for 25% flow, 8 pixels and faded. Touch up with a white brush if needed.

Possible Improvements

I’ve tried to keep this program simple and therefore easily understandable. Some additional complications that you might want to add are:

• Allow addition of areas to the mask. This was omitted because it might require adjusting the size of the BitMap bounds, but it’s not especially difficult to do. (A neater way than using BitMaps would be to represent the mask as a region, using QuickDraw routines to add to and subtract from the mask. Unfortunately, there is no standard way to convert the BitMap passed to the plug-in by SuperPaint into a region.)

• Nudge or shift a mask after creation. A little thought is needed to devise a convenient human interface for commanding this, but the operation is simple (offset the boundsRect).

• Allow the user to set the Tint pattern (e.g., as the current fill pattern, which is also passed to the plug-in by SuperPaint).

• Form the complement of a mask. In physical terms this corresponds to cutting holes from a mask and using both the mask and the cut-out portions as masks. This can be done as in the example, but it would be easier to have a new command.

Notes

These are some miscellaneous notes that may be helpful in understanding the program.

• All the Pascal files BWtc.p included in the Toolkit omit the fillPatNone and linePatNone fields, causing all later fields to be at the wrong offset. (The C and Asm files are correct.) This is corrected in the BWtc.p listing below.

• An important correspondence (not mentioned in the Toolkit documentation) is that selectRect and the GrafPort’s portRect represent the same area; selectRect is in document coordinates and portRect is in local coordinates. This correspondence allows us to map between the current selection and the document’s coordinates.

• The statement in the Toolkit documentation that the GrafPort is set to the selection is not completely correct. More accurately, the GrafPort is set to an offscreen copy of part of the document containing the selection. After the plug-in runs, SuperPaint copies the offscreen version back into the selected portion of the document.

• The lassoMaskBits record is set correctly for all selection tools other than the rectangle. The Toolkit documentation implies it is only valid for selections made with the Lasso.

[Also note, after this article was submitted, Aldus released SuperPaint 3.0 presenting two problems. The first of which is that Aldus has evidently abandoned 2.0 plug-ins by redefining the interface and providing no support for earlier interfaces. Second, 3.0 has some masking features. For the many users, who are choosing to not move to 3.0 because it is so incredibly slow, this tool is still useful. More importantly, it shows the basic methodology to using “plug-in tools”. - Ed.]

For Further Reading

If you are interested in further information on this topic, you might want to look at the following sources.

• Radu Vero, Airbrush: The Complete Studio Handbook. Watson-Guptill Publications, 1983. A good introduction to airbrush work. The example is on page 74 (exercise 14).

• Michael Ogawa, “BitMapper Utility For SuperPaint.” MacTutor, February 1990, pp. 28-40. Another example of a paint com-mand for SuperPaint. One of three related articles in this issue.

• “SuperPaint Plug-In Developer’s Toolkit.” Distributed by Silicon Beach. Send $15 to Silicon Beach Software, Inc., P.O. Box 262460, Dept. 20, San Diego, CA 92126. One disk, containing much documentation, source files defining the interfaces, and examples.

• Macintosh Technical Note #193, “So Many Bitmaps, So Little Time,” December 1989. Describes a new trap, BitMapToRegion, which is included in 32-bit QuickDraw and available separately under license from Apple. The trap is also described in Inside Macintosh, v. VI, p. 17-25.

• Ted Cohn, “Convert PICTs to Regions.” MacTutor, June 1989 (reprinted in Best of MacTutor v. 5, pp. 11-16). Gives routines for converting BitMaps and PICTs to regions.

About the author

Allen Stenger is a programmer for a large metropolitan radar house. He may be reached on AppleLink at “STENGER”.

Source Listings

Your first step is to set up a new THINK Pascal project that should look like the one in Figure 1.

Figure 1: THINK Pascal Project Window

Remember, you are writing a code resource here, so you will need to set the project type as in Figure 2.

Figure 2: Set Project Type Dialog

Listing:  BWtc.p
{File: BWtc.p}

{COPYRIGHT © 1989 Silicon Beach Software, Inc.}
{All Rights Reserved}

{ Modified by Allen Stenger, July 1991, to add }
{ missing fields fillPatNone, linePatNone }


unit BWtc;

interface

 const
 PaintOpaque = patCopy;
 PaintTransparent = patOr;
 PaintOnBlack = notPatBic;
 InvertPaint = patXor;

 { toolAbout return codes }
 noAbout = 1;
 textAbout = 2;

 {Command codes}
 menuAbout = 0;
 menuOptions = 10;
 menuSelected = 11;

 verNum = $0001;

 type

 MenuOptionsRec = record
 UsesScratch: boolean;
 end;

 MenuOptionsPtr = ^MenuOptionsRec;

 MenuDataRec = record
 toolID: integer;
 spare1: integer;
 spare2: integer;
 spare3: integer;
 spare4: integer;
 spare5: integer;
 spare6: integer;
 spare7: integer;
 shiftIsDown: boolean;
 cmdIsDown: boolean;
 optIsDown: boolean;
 spareFlag: boolean;
 fillPatNone: boolean;
 linePatNone: boolean;
 subMenu: MenuHandle;
 MenuItem: integer;
 scratchBits: BitMap;
 lassoMaskBits: BitMap;
 undoBits: BitMap; { ptr to undo bits }
 selectRect: Rect; { selection rectangle }
 { in document coords }
 curFillPat: Pattern;{ cur fill pattern }
 curPatList: Handle; { handle to currently }
 { selected pattern list}
 end;

 MenuDataPtr = ^MenuDataRec;

implementation

end.
Listing:  Masker.p
{-----------------------  }
{}
{Airbrush masker for SuperPaint 2.0a }
{}
{Written in THINK Pascal version 3.0.2 }
{}
{Allen Stenger   July 1991}
{}
{-----------------------  }

unit Masker;

interface
 uses
 BWtc;

 const

 { returnCode values for errors (we return any }
 { Memory Manager error codes as themselves)   }
 BadSelector = 1;  { bad selector value }
 InvalidSubMenu = 2; { unimplemented submenu }
 { selection }
 MaskNotInSelection = 3;  { attempt mask }
 { operation with mask not }
 { totally in selection }
 { equates for submenu selections }
 OutlineAMask = 1; { make current selection the }
 { mask }
 RemoveFromMask = 2; { remove cur selection }
 { from mask }
 { separator line = 3 }
 CaptureUnderMask = 4;    { save portion of }
 { document under mask }
 RestoreUnderMask = 5;    { restore saved data }
 { to document }
 { separator line = 6 }
 TintMask = 7    { tint mask area so we can }
 { see it }
 ForgetMask = 8; { discard all mask and saved }
 { data }

 type
 SaveArea = record { area saved by SuperPaint }
 { for us as globalData^^ }
 MaskBitMap: BitMap; { the mask, a BitMap, }
 { always maintained in }
 { document coordinates }
 SavedBitMap: BitMap;{ saved part of orig }
 { image, always maintained }
 { in document coordinates }
 Tinted: Boolean;{ mask currently tinted? }
 end;
 SaveAreaPtr = ^SaveArea;
 SaveAreaHandle = ^SaveAreaPtr;

 procedure Main (selector: integer;
 mDataPtr: MenuDataPtr;
 var globalData: SaveAreaHandle;
 var returnCode: integer);

implementation

 procedure Main;

 { look up the save area preserved by SuperPaint, }
 { or create a new one if none }
 function GetSaveArea: OSErr;
 var
 ourReturnCode: OSErr;  {holds value for GetSaveArea}
 begin
 ourReturnCode := noErr;
 if globalData = nil then
 begin
 globalData := SaveAreaHandle(NewHandle(sizeof(SaveArea)));
 ourReturnCode := MemError;
 if ourReturnCode = noErr then
 with globalData^^, globalData^^.MaskBitMap do
 begin
 baseAddr := nil;
 rowBytes := 0;
 SetRect(bounds, 0, 0, 0, 0);
 SavedBitMap := MaskBitMap;
 Tinted := false;
 end;
 end; {if globalData}
 GetSaveArea := ourReturnCode;
 end; {GetSaveArea}

 { release save area and storage it references }
 function DisposSaveArea: OSErr;
 var
 ourReturnCode: OSErr;  { holds function val }
 begin
 ourReturnCode := noErr;
 if globalData <> nil then
 begin
 with globalData^^.MaskBitMap do
 if baseAddr <> nil then
 begin
 DisposPtr(baseAddr);
 ourReturnCode := MemError;
 end;
 with globalData^^.SavedBitMap do
 if (ourReturnCode = noErr) and (baseAddr <> nil) then
 begin
 DisposPtr(baseAddr);
 ourReturnCode := MemError
 end;
 if ourReturnCode = noErr then
 begin
 DisposHandle(Handle(globalData));
 ourReturnCode := MemError
 end;
 end;
 globalData := nil;
 DisposSaveArea := ourReturnCode;
 end; {DisposSaveArea}

 { Create a bit mask in outRect within the new  }
 { BitMap outBitMap by copying the mask in inRect }
 { within inBitMap; or by filling outRect if }
 { inBitMap is null.}
 function CopyBitMask (inBitMap: BitMap;
 var outBitMap: BitMap;
 inRect, outRect: Rect): OSErr;
 var
 ourReturnCode: OSErr;  { holds value of }
 { CopyBitMask}
 il: longint;    { loop control }
 bitsSize: longint;{ size of bit array in }
 { bytes }
 byteP: PTR;{ temporary byte ptr }

 begin
 { make a new BitMap the right size to hold the mask }
 outBitMap.bounds := outRect;
 with outBitMap, outBitMap.bounds do
 begin
 rowBytes := ((right - left + 7) div 8 + 1) div 2 * 2;
 bitsSize := rowBytes * LONGINT(bottom - top);
 baseAddr := NewPtr(bitsSize);
 ourReturnCode := MemError;
 end;

 { blit the mask into the new BitMap }
 if ourReturnCode = noErr then
 if inBitMap.baseAddr <> nil then
 with outBitMap, outBitMap.bounds do
 CopyBits(inBitMap, outBitMap, inRect, bounds, srcCopy, nil)
 else
 with outBitMap do
 for il := 0 to bitsSize - 1 do
 begin
 byteP := PTR(ORD(baseAddr) + il);
 byteP^ := 255;
 end; {for il}

 CopyBitMask := ourReturnCode;
 end; {CopyBitMask}

 { convert r from Document to GrafPort (local) }
 { coordinates }
 procedure DocToGraf (r: Rect;
 gPtr: GrafPtr);
 begin
 with mDataPtr^, gPtr^ do
 OffsetRect(r, -(selectRect.left - portRect.left), -(selectRect.top - 
portRect.top));
 end; {DocToGraf}

 { Flip (i.e. XOR) the tint of the mask }
 procedure FlipTint (gPtr: GrafPtr);
 var
 workRect: Rect; { temp Rectangle }
 begin
 workRect := globalData^^.MaskBitMap.bounds;
 DocToGraf(workRect, gPtr);
 CopyBits(globalData^^.MaskBitMap, gPtr^.portBits, globalData^^.MaskBitMap.bounds, 
workRect, srcXor, nil);
 end; {FlipTint}

 { execute the desired action from the menu }
 procedure TakeAction;
 var
 i: integer;{ loop control }
 gPtr: GrafPtr;  { points to current }
 { GrafPort }
 workRect: Rect; { temporary working }
 { rectangle }
 bitsSize: longint;{ size of bit array }
 { in bytes }
 workBitMap: BitMap; { temporary bit map }
 trashB: Boolean;{ temporary Boolean }

 begin
 GetPort(gPtr);

 case mDataPtr^.menuItem of
 OutlineAMask, ForgetMask: 
 begin
 returnCode := DisposSaveArea;
 if returnCode = noErr then
 returnCode := GetSaveArea
 end;
 otherwise
 returnCode := GetSaveArea;
 end; {case mDataPtr^.menuItem}

 if returnCode = noErr then
 case mDataPtr^.menuItem of

 OutlineAMask: 
 begin
 returnCode := CopyBitMask(mDataPtr^.lassoMaskBits, globalData^^.MaskBitMap, 
gPtr^.portRect, mDataPtr^.selectRect);
 end; {OutlineAMask}

 RemoveFromMask: 
 begin
 returnCode := CopyBitMask(mDataPtr^.lassoMaskBits, workBitMap, gPtr^.portRect, 
mDataPtr^.selectRect);
 if returnCode = noErr then
 begin
 workRect := workBitMap.bounds;
 if globalData^^.Tinted then
 FlipTint(gPtr); { remove Tint where }
 { mask will be removed}
 CopyBits(workBitMap, globalData^^.MaskBitMap, workRect, workRect, srcBic, 
nil);
 DisposPtr(workBitMap.baseAddr);
 returnCode := MemError;
 workBitMap.baseAddr := nil;
 end  {if returnCode}
 end; {RemoveFromMask}

 CaptureUnderMask: 
 begin
 { Quick and dirty check to see }
 {if user’s selection includes the mask area. }
 { A better check would check that all }
 { bits in MaskBitMap are in lassoMaskBits. }
 trashB := SectRect(mDataPtr^.selectRect, globalData^^.MaskBitMap.bounds, 
workRect);
 if not EqualRect(workRect, globalData^^.MaskBitMap.bounds) then
 returnCode := MaskNotInSelection
 else
 begin
 with globalData^^, globalData^^.SavedBitMap do
 begin
 if baseAddr <> nil then
 DisposPtr(baseAddr);{ discard any }
 { previous capture }
 SavedBitMap := MaskBitMap;
 baseAddr := NewPtr(GetPtrSize(MaskBitMap.baseAddr));
 returnCode := MemError;
 if returnCode = noErr then
 begin
 workRect := bounds;
 DocToGraf(workRect, gPtr);
 CopyBits(gPtr^.portBits, SavedBitMap, workRect, bounds, srcCopy, nil);
 end; {if returnCode}
 end; {with globalData^^}
 end; {if not EqualRect}
 end; {CaptureUnderMask}

 RestoreUnderMask: 
 begin
 workRect := globalData^^.SavedBitMap.bounds;
 DocToGraf(workRect, gPtr);
 with globalData^^, gPtr^ do
 CopyMask(SavedBitMap, MaskBitMap, portBits, SavedBitMap.bounds, MaskBitMap.bounds, 
workRect);
 globalData^^.Tinted := false;{we obliterated tint}
 returnCode := noErr;
 end; {RestoreUnderMask}

 TintMask: 
 begin
 FlipTint(gPtr);
 with globalData^^ do
 Tinted := not Tinted;
 returnCode := noErr;
 end; {TintMask}

 ForgetMask: 
 begin
 returnCode := noErr;
 end; {ForgetMask}

 otherwise
 returnCode := InvalidSubMenu;

 end; {case mDataPtr^.subMenu}

 end; {TakeAction}

 { update valid menu selections }
 procedure UpdateMenu;
 begin
 with globalData^^, mDataPtr^ do
 begin
 if MaskBitMap.baseAddr = nil then
 begin
 DisableItem(subMenu, RemoveFromMask);
 DisableItem(subMenu, CaptureUnderMask);
 DisableItem(subMenu, TintMask);
 DisableItem(subMenu, ForgetMask)
 end
 else
 begin
 EnableItem(subMenu, RemoveFromMask);
 EnableItem(subMenu, CaptureUnderMask);
 EnableItem(subMenu, TintMask);
 EnableItem(subMenu, ForgetMask)
 end;
 if SavedBitMap.baseAddr = nil then
 DisableItem(subMenu, RestoreUnderMask)
 else
 EnableItem(subMenu, RestoreUnderMask);
 if Tinted then
 DisableItem(subMenu, CaptureUnderMask);
 CheckItem(subMenu, TintMask, Tinted);
 end; {with globalData^^}
 end; {UpdateMenu}

 begin {Main}
 case selector of

 menuAbout: 
 begin
 returnCode := textAbout;
 {our About  is in TEXT resource}
 end;

 menuOptions: 
 begin
 MenuOptionsPtr(mDataPtr)^.usesScratch := false;
 {we don’t use scratch area}
 returnCode := noErr;
 end;

 menuSelected: 
 begin
 TakeAction;
 UpdateMenu;
 if returnCode <> noErr then
 SysBeep(1);
 { SuperPaint doesn’t notify user of error, }
 { so we will}
 end;

 otherwise
 begin
 returnCode := BadSelector
 end;

 end; {case selector of}

 end; {Main}

end.
Listing:  Masker.r
/*------------------------------------------- */
/* Resource definition file (Rez) for Masker  */
/* Allen Stenger    July 1991                 */
/*------------------------------------------- */

/* This SICN appears in the About  plug-in list. */
resource ‘SICN’ (16000, purgeable) {
 { /* array: 1 elements */
 /* [1] */
 $”0780 1FE0 3870 E01C 8004 BFF4 E31C EB5C”
 $”A314 BFF4 8004 8004 8004 FFFC 1860 1860”
 }
};

/* This resource defines the interface version */
data ‘PiMI’ (16000, purgeable, preload) {
 $”0001”               
};

/* This menu is shown an a hierarchical menu to */
/* the Masker command. */
resource ‘MENU’ (16000, purgeable) {
 128,
 textMenuProc,
 0x7FFFFF01,
 enabled,
 “Masker”,
 { /* array: 8 elements */
 /* [1] */
 “Outline a Mask”, noIcon, noKey, noMark, plain,
 /* [2] */
 “Remove from Mask”, noIcon, noKey, noMark, plain,
 /* [3] */
 “-”, noIcon, noKey, noMark, plain,
 /* [4] */
 “Capture under Mask”, noIcon, noKey, noMark, plain,
 /* [5] */
 “Restore under Mask”, noIcon, noKey, noMark, plain,
 /* [6] */
 “-”, noIcon, noKey, noMark, plain,
 /* [7] */
 “Tint Mask”, noIcon, noKey, noMark, plain,
 /* [8] */
 “Forget Mask”, noIcon, noKey, noMark, plain
 }
};

/* This text is shown as the About  info for Masker. */
data ‘TEXT’ (16000, “About ”, purgeable) {
“Masker Menu Command - Written by Allen “
“Stenger, July 1991\n\n”
“Use this command to protect an area from “
“being painted over. The steps are:\n”
“  1. Define the “mask” by selecting the “
“area to be protected with any of the “ 
“selection tools, then command Outline “
“A Mask. Pieces may then be cut from the “
“mask by selecting them and commanding “
“Remove From Mask.\n”
“  2. Save the area under the mask by “
“selecting an area containing the mask “ 
“and commanding Capture Under Mask.\n”
“  3. Paint as desired.\n”
“  4. Restore the area under the mask by “
“selecting an area containing the mask “
“and commanding Restore Under Mask.\n\n”
“The Tint Mask command tints the mask “
“so it can be seen. Issue Tint Mask “
“again to remove the tint. Capture “
“is disabled while Tint is on, to “
“prevent the tint from being captured too.\n\n”
“The Forget Mask command is not normally “
“needed, since Outline A Mask will clear “
“the previous mask; it may be used to “
“release the memory occupied by a mask.\n\n”
“Menu commands can only work on areas “
“within the current selection, so be sure “
“the entire area of the mask is selected “
“before Tint, Capture, or Restore.”
};
 

Community Search:
MacTech Search:

Software Updates via MacUpdate

EtreCheck 3.1.5 - For troubleshooting yo...
EtreCheck is an app that displays the important details of your system configuration and allow you to copy that information to the Clipboard. It is meant to be used with Apple Support Communities to... Read more
WALTR 2 2.0.8 - $39.95
WALTR 2 helps you wirelessly drag-and-drop any music, ringtones, videos, PDF, and ePub files onto your iPhone, iPad, or iPod without iTunes. It is the second major version of Softorino's critically-... Read more
Carbon Copy Cloner 4.1.12 - Easy-to-use...
Carbon Copy Cloner backups are better than ordinary backups. Suppose the unthinkable happens while you're under deadline to finish a project: your Mac is unresponsive and all you hear is an ominous,... Read more
Dropbox 16.3.27 - Cloud backup and synch...
Dropbox is an application that creates a special Finder folder that automatically syncs online and between your computers. It allows you to both backup files and keep them up-to-date between systems... Read more
Microsoft OneNote 15.29 - Free digital n...
OneNote is your very own digital notebook. With OneNote, you can capture that flash of genius, that moment of inspiration, or that list of errands that's too important to forget. Whether you're at... Read more
Spotify 1.0.44.100. - Stream music, crea...
Spotify is a streaming music service that gives you on-demand access to millions of songs. Whether you like driving rock, silky R&B, or grandiose classical music, Spotify's massive catalogue puts... Read more
SpamSieve 2.9.27 - Robust spam filter fo...
SpamSieve is a robust spam filter for major email clients that uses powerful Bayesian spam filtering. SpamSieve understands what your spam looks like in order to block it all, but also learns what... Read more
VueScan 9.5.62 - Scanner software with a...
VueScan is a scanning program that works with most high-quality flatbed and film scanners to produce scans that have excellent color fidelity and color balance. VueScan is easy to use, and has... Read more
Fantastical 2.3.2 - Create calendar even...
Fantastical 2 is the Mac calendar you'll actually enjoy using. Creating an event with Fantastical is quick, easy, and fun: Open Fantastical with a single click or keystroke Type in your event... Read more
PCalc 4.4.4 - Full-featured scientific c...
PCalc is a full-featured, scriptable scientific calculator with support for hexadecimal, octal, and binary calculations, as well as an RPN mode, programmable functions, and an extensive set of unit... Read more

Latest Forum Discussions

See All

Track Santa with these three festive app...
Christmas is fast approaching and that means it's time to prepare for Santa's yearly pilgrimage around the globe. Christmas Eve is an exciting time as parents help their kids get ready to welcome Santa. You've got the cookies and milk all planned... | Read more »
Galaxy on Fire 3 and four other fantasti...
Galaxy on Fire 3 - Manticore brings the series back for another round of daring space battles. It's familiar territory for folks who are familiar with the franchise. If you've beaten the game and are looking to broaden your horizons, might we... | Read more »
The best apps for your holiday gift exch...
What's that, you say? You still haven't started your holiday shopping? Don't beat yourself up over it -- a lot of people have been putting it off, too. It's become easier and easier to procrastinate gift shopping thanks to a number of apps that... | Read more »
Toca Hair Salon 3 (Education)
Toca Hair Salon 3 1.0 Device: iOS Universal Category: Education Price: $2.99, Version: 1.0 (iTunes) Description: | Read more »
Winter comes to Darkwood as Seekers Note...
MyTona, based in the chilly Siberian city of Yakutsk, has brought a little festive fun to its hidden object game Seekers Notes: Hidden Mystery. The Christmas update introduces some new inhabitants to players, and with them a chance to win plenty of... | Read more »
Bully: Anniversary Edition (Games)
Bully: Anniversary Edition 1.03.1 Device: iOS Universal Category: Games Price: $6.99, Version: 1.03.1 (iTunes) Description: *** PLEASE NOTE: This game is officially supported on the following devices: iPhone 5 and newer, iPod Touch... | Read more »
PINE GROVE (Games)
PINE GROVE 1.0 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0 (iTunes) Description: A pine grove where there are no footsteps of people due to continuous missing cases. The case is still unsolved and nothing has... | Read more »
Niantic teases new Pokémon announcement...
After rumors started swirling yesterday, it turns out there is an official Pokémon GO update on its way. We’ll find out what’s in store for us and our growing Pokémon collections tomorrow during the Starbucks event, but Niantic will be revealing... | Read more »
3 reasons why Nicki Minaj: The Empire is...
Nicki Minaj is as business-savvy as she is musically talented and she’s proved that by launching her own game. Designed by Glu, purveyors of other fine celebrity games like cult favorite Kim Kardashian: Hollywood, Nicki Minaj: The Empire launched... | Read more »
Clash of Clans is getting its own animat...
Riding on its unending wave of fame and success, Clash of Clans is getting an animated web series based on its Clash-A-Rama animated shorts.As opposed to the current shorts' 60 second run time, the new and improved Clash-A-Rama will be comprised of... | Read more »

Price Scanner via MacPrices.net

Never Settle for Low Performing Wifi With iOS...
AppYogi Software has announced the release of WiFi Signal Strength Status App 1.0, the company’s new utility developed exclusively for macOS. WiFi Signal Strength Status App features a unique, single... Read more
New 2016 13-inch Touch Bar MacBook Pros in st...
B&H Photo has stock of new 2016 Apple 13″ Touch Bar MacBook Pro models, each including free shipping plus NY sales tax only: - 13″ 2.9GHz/512GB Touch Bar MacBook Pro Space Gray: $1999 - 13″ 2.... Read more
New 2016 15″ Touch Bar MacBook Pros in stock...
B&H Photo has new 2016 Apple 15″ Touch Bar MacBook Pro models in stock today including free shipping plus NY sales tax only: - 15″ 2.7GHz Touch Bar MacBook Pro Space Gray: $2799 - 15″ 2.7GHz... Read more
DietSensor App Targeting Diabetes and Obesity...
DietSensor, Inc., a developer of smart food and nutrition applications designed to fight diabetes and obesity and help improve overall fitness, has announced the launch of its DietSensor app for... Read more
Holiday 2016 13-inch 2.0GHz MacBook Pro sales...
B&H has the non-Touch Bar 13″ MacBook Pros in stock today for $50-$100 off MSRP. Shipping is free, and B&H charges NY sales tax only: - 13″ 2.0GHz MacBook Pro Space Gray (MLL42LL/A): $1449 $... Read more
Holiday sale: Apple TVs for $51-$40 off MSRP,...
Best Buy has dropped their price on the 64GB Apple TV to $159.99 including free shipping. That’s $40 off MSRP. 32GB Apple TVs are on sale right now for $98 on Sams Club’s online store. That’s $51 off... Read more
12-inch Retina MacBooks, Apple refurbished, n...
Apple has restocked a full line of Certified Refurbished 2016 12″ Retina MacBooks, now available for $200-$260 off MSRP. Refurbished 2015 models are available starting at $929. Apple will include a... Read more
Holiday sale: 12-inch Retina MacBook for $100...
B&H has 12″ Retina MacBooks on sale for $100 off MSRP as part of their Holiday sale. Shipping is free, and B&H charges NY sales tax only: - 12″ 1.1GHz Space Gray Retina MacBook: $1199 $100... Read more
Apple refurbished 13-inch MacBook Airs availa...
Apple has Certified Refurbished 13″ MacBook Airs available starting at $849. An Apple one-year warranty is included with each MacBook, and shipping is free: - 13″ 1.6GHz/8GB/128GB MacBook Air: $849 $... Read more
Apple refurbished iMacs available for up to $...
Apple has Certified Refurbished 2015 21″ & 27″ iMacs available for up to $350 off MSRP. Apple’s one-year warranty is standard, and shipping is free. The following models are available: - 21″ 3.... Read more

Jobs Board

*Apple* Retail - Multiple Positions - Apple,...
Job Description: Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
US- *Apple* Store Leader Program - Apple (Un...
…Summary Learn and grow as you explore the art of leadership at the Apple Store. You'll master our retail business inside and out through training, hands-on Read more
Automotive Detailer - *Apple* Used Autos -...
We are currently conductinginterviews and will be accepting applications for a part-time detailer. Apple Used Autos is a great place to work andstart a career. We Read more
*Apple* Retail - Multiple Positions - Apple,...
Job Description: Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
*Apple* Retail - Multiple Positions- Trumbul...
Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, you're also the Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.