1/*
2    NSBitmapImageRep_PPUtilities_ImageBitmaps.m
3
4    Copyright 2013-2018 Josh Freeman
5    http://www.twilightedge.com
6
7    This file is part of PikoPixel for Mac OS X and GNUstep.
8    PikoPixel is a graphical application for drawing & editing pixel-art images.
9
10    PikoPixel is free software: you can redistribute it and/or modify it under
11    the terms of the GNU Affero General Public License as published by the
12    Free Software Foundation, either version 3 of the License, or (at your
13    option) any later version approved for PikoPixel by its copyright holder (or
14    an authorized proxy).
15
16    PikoPixel is distributed in the hope that it will be useful, but WITHOUT ANY
17    WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18    FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
19    details.
20
21    You should have received a copy of the GNU Affero General Public License
22    along with this program. If not, see <http://www.gnu.org/licenses/>.
23*/
24
25#import "NSBitmapImageRep_PPUtilities.h"
26
27#import "PPDefines.h"
28#import "PPGeometry.h"
29#import "NSColor_PPUtilities.h"
30#import "NSImage_PPUtilities.h"
31
32
33#define kImageBitmapBitsPerSample                                           \
34            (sizeof(PPImagePixelComponent) * 8)
35
36#define kImageBitmapSamplesPerPixel                                         \
37            (sizeof(PPImageBitmapPixel) / sizeof(PPImagePixelComponent))
38
39
40#define kMaxScalingFactorToForceDotsGridType                6
41#define kCrosshairLegSizeToScalingFactorRatio               (1.0/7.0)
42
43
44@implementation NSBitmapImageRep (PPUtilities_ImageBitmaps)
45
46+ (NSBitmapImageRep *) ppImageBitmapOfSize: (NSSize) size
47{
48    NSBitmapImageRep *imageBitmap;
49
50    size = PPGeometry_SizeClippedToIntegerValues(size);
51
52    if (PPGeometry_IsZeroSize(size))
53    {
54        goto ERROR;
55    }
56
57    imageBitmap = [[[NSBitmapImageRep alloc] initWithBitmapDataPlanes: NULL
58                                                pixelsWide: size.width
59                                                pixelsHigh: size.height
60                                                bitsPerSample: kImageBitmapBitsPerSample
61                                                samplesPerPixel: kImageBitmapSamplesPerPixel
62                                                hasAlpha: YES
63                                                isPlanar: NO
64                                                colorSpaceName: NSCalibratedRGBColorSpace
65                                                bytesPerRow: 0
66                                                bitsPerPixel: 0]
67                                        autorelease];
68
69    if (!imageBitmap)
70        goto ERROR;
71
72    // use sRGB colorspace
73    [imageBitmap ppAttachSRGBColorProfile];
74
75    [imageBitmap ppClearBitmap];
76
77    return imageBitmap;
78
79ERROR:
80    return nil;
81}
82
83+ (NSBitmapImageRep *) ppImageBitmapWithImportedData: (NSData *) importedData
84{
85    return [[NSBitmapImageRep imageRepWithData: importedData] ppImageBitmap];
86}
87
88+ (NSBitmapImageRep *) ppImageBitmapFromImageResource: (NSString *) imageName
89{
90    NSString *imageResourcePath;
91    NSData *imageData;
92
93    if (!imageName)
94        goto ERROR;
95
96    imageResourcePath = [[NSBundle mainBundle] pathForImageResource: imageName];
97
98    if (!imageResourcePath)
99        goto ERROR;
100
101    imageData = [NSData dataWithContentsOfFile: imageResourcePath];
102
103    if (!imageData)
104        goto ERROR;
105
106    return [NSBitmapImageRep ppImageBitmapWithImportedData: imageData];
107
108ERROR:
109    return nil;
110}
111
112- (bool) ppIsImageBitmap
113{
114    return (([self samplesPerPixel] == kImageBitmapSamplesPerPixel)
115                && ([self bitsPerSample] == kImageBitmapBitsPerSample))
116            ? YES : NO;
117}
118
119- (bool) ppIsImageBitmapAndSameSizeAsMaskBitmap: (NSBitmapImageRep *) maskBitmap
120{
121    if ([self ppIsImageBitmap]
122        && [maskBitmap ppIsMaskBitmap]
123        && NSEqualSizes([self ppSizeInPixels], [maskBitmap ppSizeInPixels]))
124    {
125        return YES;
126    }
127    else
128    {
129        return NO;
130    }
131}
132
133// ppImageColorAtPoint: is used instead of the Cocoa-native -[NSBitmapImageRep colorAtX:y:],
134// because it returns image-bitmap colors in the correct colorspace (sRGB); As of OS X 10.6,
135// colorAtX:y: incorrectly ignores image-bitmaps' custom colorspace, returning colors in
136// NSCalibratedRGBColorSpace
137
138- (NSColor *) ppImageColorAtPoint: (NSPoint) point
139{
140    NSRect bitmapFrame;
141    unsigned char *bitmapData;
142    int rowOffset, dataOffset;
143    PPImageBitmapPixel *bitmapPixel;
144    CGFloat alphaComponent, redValue, greenValue, blueValue, alphaValue;
145
146    if (![self ppIsImageBitmap])
147    {
148        goto ERROR;
149    }
150
151    bitmapFrame = [self ppFrameInPixels];
152
153    point = PPGeometry_PointClippedToRect(PPGeometry_PointClippedToIntegerValues(point),
154                                            bitmapFrame);
155
156    bitmapData = [self bitmapData];
157
158    if (!bitmapData)
159        goto ERROR;
160
161    rowOffset = bitmapFrame.size.height - 1 - point.y;
162
163    dataOffset = rowOffset * [self bytesPerRow] + point.x * sizeof(PPImageBitmapPixel);
164
165    bitmapPixel = (PPImageBitmapPixel *) &bitmapData[dataOffset];
166
167    alphaComponent = macroImagePixelComponent_Alpha(bitmapPixel);
168
169    if (alphaComponent > 0)
170    {
171        // nonzero alpha: un-premultiply RGB components
172        redValue= ((CGFloat) macroImagePixelComponent_Red(bitmapPixel)) / alphaComponent;
173
174        greenValue = ((CGFloat) macroImagePixelComponent_Green(bitmapPixel)) / alphaComponent;
175
176        blueValue = ((CGFloat) macroImagePixelComponent_Blue(bitmapPixel)) / alphaComponent;
177
178        alphaValue = alphaComponent / ((CGFloat) kMaxImagePixelComponentValue);
179    }
180    else
181    {
182        // zero alpha: all components are zero due to premultiply
183        redValue = greenValue = blueValue = alphaValue = 0;
184    }
185
186    return [NSColor ppSRGBColorWithRed: redValue
187                                green: greenValue
188                                blue: blueValue
189                                alpha: alphaValue];
190
191ERROR:
192    return nil;
193}
194
195- (bool) ppImageBitmapHasTransparentPixels
196{
197    unsigned char *bitmapRow;
198    int bytesPerRow, pixelsPerRow, rowCounter, pixelCounter;
199    NSSize bitmapSize;
200    PPImageBitmapPixel *bitmapPixel;
201
202    if (![self ppIsImageBitmap])
203    {
204        goto ERROR;
205    }
206
207    bitmapRow = [self bitmapData];
208
209    if (!bitmapRow)
210        goto ERROR;
211
212    bytesPerRow = [self bytesPerRow];
213
214    bitmapSize = [self ppSizeInPixels];
215
216    pixelsPerRow = bitmapSize.width;
217    rowCounter = bitmapSize.height;
218
219    while (rowCounter--)
220    {
221        bitmapPixel = (PPImageBitmapPixel *) bitmapRow;
222        pixelCounter = pixelsPerRow;
223
224        while (pixelCounter--)
225        {
226            if (macroImagePixelComponent_Alpha(bitmapPixel) != kMaxImagePixelComponentValue)
227            {
228                return YES;
229            }
230
231            bitmapPixel++;
232        }
233
234        bitmapRow += bytesPerRow;
235    }
236
237    return NO;
238
239ERROR:
240    return NO;
241}
242
243- (void) ppMaskedFillUsingMask: (NSBitmapImageRep *) maskBitmap
244            inBounds: (NSRect) fillBounds
245            fillPixelValue: (PPImageBitmapPixel) fillPixelValue
246{
247    NSRect bitmapFrame;
248    unsigned char *destinationData, *maskData, *destinationRow, *maskRow;
249    int destinationBytesPerRow, maskBytesPerRow, rowOffset, destinationDataOffset,
250            maskDataOffset, pixelsPerRow, rowCounter, pixelCounter;
251    PPImageBitmapPixel *destinationPixel;
252    PPMaskBitmapPixel *maskPixel;
253
254    if (![self ppIsImageBitmapAndSameSizeAsMaskBitmap: maskBitmap])
255    {
256        goto ERROR;
257    }
258
259    bitmapFrame = [self ppFrameInPixels];
260
261    fillBounds =
262            NSIntersectionRect(PPGeometry_PixelBoundsCoveredByRect(fillBounds), bitmapFrame);
263
264    if (NSIsEmptyRect(fillBounds))
265    {
266        goto ERROR;
267    }
268
269    destinationData = [self bitmapData];
270    maskData = [maskBitmap bitmapData];
271
272    if (!destinationData || !maskData)
273    {
274        goto ERROR;
275    }
276
277    rowOffset = bitmapFrame.size.height - fillBounds.size.height - fillBounds.origin.y;
278
279    destinationBytesPerRow = [self bytesPerRow];
280    destinationDataOffset =
281        rowOffset * destinationBytesPerRow + fillBounds.origin.x * sizeof(PPImageBitmapPixel);
282    destinationRow = &destinationData[destinationDataOffset];
283
284    maskBytesPerRow = [maskBitmap bytesPerRow];
285    maskDataOffset =
286            rowOffset * maskBytesPerRow + fillBounds.origin.x * sizeof(PPMaskBitmapPixel);
287    maskRow = &maskData[maskDataOffset];
288
289    pixelsPerRow = fillBounds.size.width;
290    rowCounter = fillBounds.size.height;
291
292    while (rowCounter--)
293    {
294        destinationPixel = (PPImageBitmapPixel *) destinationRow;
295        maskPixel = (PPMaskBitmapPixel *) maskRow;
296
297        pixelCounter = pixelsPerRow;
298
299        while (pixelCounter--)
300        {
301            if (*maskPixel++)
302            {
303                *destinationPixel = fillPixelValue;
304            }
305
306            destinationPixel++;
307        }
308
309        destinationRow += destinationBytesPerRow;
310        maskRow += maskBytesPerRow;
311    }
312
313    return;
314
315ERROR:
316    return;
317}
318
319- (void) ppMaskedEraseUsingMask: (NSBitmapImageRep *) maskBitmap
320{
321    [self ppMaskedFillUsingMask: maskBitmap
322            inBounds: [self ppFrameInPixels]
323            fillPixelValue: 0];
324}
325
326- (void) ppMaskedEraseUsingMask: (NSBitmapImageRep *) maskBitmap
327            inBounds: (NSRect) eraseBounds
328{
329    [self ppMaskedFillUsingMask: maskBitmap
330            inBounds: eraseBounds
331            fillPixelValue: 0];
332}
333
334- (void) ppMaskedCopyFromImageBitmap: (NSBitmapImageRep *) sourceBitmap
335            usingMask: (NSBitmapImageRep *) maskBitmap
336{
337    return [self ppMaskedCopyFromImageBitmap: sourceBitmap
338                    usingMask: maskBitmap
339                    inBounds: [self ppFrameInPixels]];
340}
341
342- (void) ppMaskedCopyFromImageBitmap: (NSBitmapImageRep *) sourceBitmap
343            usingMask: (NSBitmapImageRep *) maskBitmap
344            inBounds: (NSRect) copyBounds
345{
346    NSRect bitmapFrame;
347    unsigned char *destinationData, *sourceData, *maskData, *destinationRow, *sourceRow,
348                    *maskRow;
349    int destinationBytesPerRow, sourceBytesPerRow, maskBytesPerRow, rowOffset,
350            destinationDataOffset, sourceDataOffset, maskDataOffset,
351            pixelsPerRow, rowCounter, pixelCounter;
352    PPImageBitmapPixel *destinationPixel, *sourcePixel;
353    PPMaskBitmapPixel *maskPixel;
354
355    if (![self ppIsImageBitmap]
356        || ![sourceBitmap ppIsImageBitmapAndSameSizeAsMaskBitmap: maskBitmap])
357    {
358        goto ERROR;
359    }
360
361    bitmapFrame = [self ppFrameInPixels];
362
363    if (!NSEqualSizes(bitmapFrame.size, [sourceBitmap ppSizeInPixels]))
364    {
365        goto ERROR;
366    }
367
368    copyBounds =
369            NSIntersectionRect(PPGeometry_PixelBoundsCoveredByRect(copyBounds), bitmapFrame);
370
371    if (NSIsEmptyRect(copyBounds))
372    {
373        goto ERROR;
374    }
375
376    destinationData = [self bitmapData];
377    sourceData = [sourceBitmap bitmapData];
378    maskData = [maskBitmap bitmapData];
379
380    if (!destinationData || !sourceData || !maskData)
381    {
382        goto ERROR;
383    }
384
385    destinationBytesPerRow = [self bytesPerRow];
386    sourceBytesPerRow = [sourceBitmap bytesPerRow];
387    maskBytesPerRow = [maskBitmap bytesPerRow];
388
389    rowOffset = bitmapFrame.size.height - copyBounds.size.height - copyBounds.origin.y;
390
391    destinationDataOffset =
392        rowOffset * destinationBytesPerRow + copyBounds.origin.x * sizeof(PPImageBitmapPixel);
393
394    sourceDataOffset =
395        rowOffset * sourceBytesPerRow + copyBounds.origin.x * sizeof(PPImageBitmapPixel);
396
397    maskDataOffset =
398        rowOffset * maskBytesPerRow + copyBounds.origin.x * sizeof(PPMaskBitmapPixel);
399
400    destinationRow = &destinationData[destinationDataOffset];
401    sourceRow = &sourceData[sourceDataOffset];
402    maskRow = &maskData[maskDataOffset];
403
404    pixelsPerRow = copyBounds.size.width;
405    rowCounter = copyBounds.size.height;
406
407    while (rowCounter--)
408    {
409        destinationPixel = (PPImageBitmapPixel *) destinationRow;
410        sourcePixel = (PPImageBitmapPixel *) sourceRow;
411        maskPixel = (PPMaskBitmapPixel *) maskRow;
412
413        pixelCounter = pixelsPerRow;
414
415        while (pixelCounter--)
416        {
417            if (*maskPixel++)
418            {
419                *destinationPixel = *sourcePixel;
420            }
421
422            destinationPixel++;
423            sourcePixel++;
424        }
425
426        destinationRow += destinationBytesPerRow;
427        sourceRow += sourceBytesPerRow;
428        maskRow += maskBytesPerRow;
429    }
430
431    return;
432
433ERROR:
434    return;
435}
436
437- (void) ppMaskedCopyFromImageBitmap:(NSBitmapImageRep *) sourceBitmap
438            usingMask: (NSBitmapImageRep *) maskBitmap
439            toPoint: (NSPoint) targetPoint
440{
441    NSRect destinationFrame, sourceFrame, destinationCopyBounds, sourceCopyBounds;
442    unsigned char *destinationData, *sourceData, *maskData, *destinationRow, *sourceRow,
443                    *maskRow;
444    int destinationBytesPerRow, sourceBytesPerRow, maskBytesPerRow, rowOffset,
445            destinationDataOffset, sourceDataOffset, maskDataOffset,
446            pixelsPerRow, rowCounter, pixelCounter;
447    PPImageBitmapPixel *destinationPixel, *sourcePixel;
448    PPMaskBitmapPixel *maskPixel;
449
450    if (![self ppIsImageBitmap]
451        || ![sourceBitmap ppIsImageBitmapAndSameSizeAsMaskBitmap: maskBitmap])
452    {
453        goto ERROR;
454    }
455
456    targetPoint = PPGeometry_PointClippedToIntegerValues(targetPoint);
457
458    destinationFrame = [self ppFrameInPixels];
459    sourceFrame = [sourceBitmap ppFrameInPixels];
460
461    destinationCopyBounds =
462                NSIntersectionRect(NSOffsetRect(sourceFrame, targetPoint.x, targetPoint.y),
463                                    destinationFrame);
464
465    if (NSIsEmptyRect(destinationCopyBounds))
466    {
467        goto ERROR;
468    }
469
470    sourceCopyBounds = NSOffsetRect(destinationCopyBounds, -targetPoint.x, -targetPoint.y);
471
472    destinationData = [self bitmapData];
473    sourceData = [sourceBitmap bitmapData];
474    maskData = [maskBitmap bitmapData];
475
476    if (!destinationData || !sourceData || !maskData)
477    {
478        goto ERROR;
479    }
480
481    destinationBytesPerRow = [self bytesPerRow];
482    sourceBytesPerRow = [sourceBitmap bytesPerRow];
483    maskBytesPerRow = [maskBitmap bytesPerRow];
484
485    rowOffset = destinationFrame.size.height - destinationCopyBounds.size.height
486                - destinationCopyBounds.origin.y;
487
488    destinationDataOffset = rowOffset * destinationBytesPerRow
489                            + destinationCopyBounds.origin.x * sizeof(PPImageBitmapPixel);
490
491    rowOffset = sourceFrame.size.height - sourceCopyBounds.size.height
492                - sourceCopyBounds.origin.y;
493
494    sourceDataOffset = rowOffset * sourceBytesPerRow
495                        + sourceCopyBounds.origin.x * sizeof(PPImageBitmapPixel);
496
497    maskDataOffset = rowOffset * maskBytesPerRow
498                        + sourceCopyBounds.origin.x * sizeof(PPMaskBitmapPixel);
499
500    destinationRow = &destinationData[destinationDataOffset];
501    sourceRow = &sourceData[sourceDataOffset];
502    maskRow = &maskData[maskDataOffset];
503
504    pixelsPerRow = destinationCopyBounds.size.width;
505    rowCounter = destinationCopyBounds.size.height;
506
507    while (rowCounter--)
508    {
509        destinationPixel = (PPImageBitmapPixel *) destinationRow;
510        sourcePixel = (PPImageBitmapPixel *) sourceRow;
511        maskPixel = (PPMaskBitmapPixel *) maskRow;
512
513        pixelCounter = pixelsPerRow;
514
515        while (pixelCounter--)
516        {
517            if (*maskPixel++)
518            {
519                *destinationPixel = *sourcePixel;
520            }
521
522            destinationPixel++;
523            sourcePixel++;
524        }
525
526        destinationRow += destinationBytesPerRow;
527        sourceRow += sourceBytesPerRow;
528        maskRow += maskBytesPerRow;
529    }
530
531    return;
532
533ERROR:
534    return;
535}
536
537- (void) ppScaledCopyFromImageBitmap: (NSBitmapImageRep *) sourceBitmap
538            inRect: (NSRect) sourceRect
539            toPoint: (NSPoint) destinationPoint
540            scalingFactor: (unsigned) scalingFactor
541{
542    NSRect destinationRect, sourceBitmapFrame, destinationBitmapFrame;
543    unsigned char *sourceData, *destinationData, *scaledRowData, *sourceRow, *destinationRow;
544    int sourceBytesPerRow, numSourceRowsToSkip, sourceDataOffset, destinationBytesPerRow,
545        numDestinationRowsToSkip, destinationDataOffset, scaledRowDataSize,
546        numTimesToCopyScaledRow, pixelsPerRow, rowCounter, pixelCounter, scaleCounter;
547    PPImageBitmapPixel *currentSourcePixel, *currentScaledPixel;
548
549    if (scalingFactor == 1)
550    {
551        [self ppCopyFromBitmap: sourceBitmap
552                inRect: sourceRect
553                toPoint: destinationPoint];
554
555        return;
556    }
557
558    if (![self ppIsImageBitmap] || ![sourceBitmap ppIsImageBitmap]
559        || (scalingFactor < 1))
560    {
561        goto ERROR;
562    }
563
564    sourceRect = PPGeometry_PixelBoundsCoveredByRect(sourceRect);
565
566    if (NSIsEmptyRect(sourceRect))
567    {
568        goto ERROR;
569    }
570
571    destinationRect.origin = PPGeometry_PointClippedToIntegerValues(destinationPoint);
572    destinationRect.size = NSMakeSize(sourceRect.size.width * scalingFactor,
573                                        sourceRect.size.height * scalingFactor);
574
575    sourceBitmapFrame = [sourceBitmap ppFrameInPixels];
576    destinationBitmapFrame = [self ppFrameInPixels];
577
578    if (!NSContainsRect(sourceBitmapFrame, sourceRect)
579        || !NSContainsRect(destinationBitmapFrame, destinationRect))
580    {
581        goto ERROR;
582    }
583
584    sourceData = [sourceBitmap bitmapData];
585    destinationData = [self bitmapData];
586
587    if (!sourceData || !destinationData)
588    {
589        goto ERROR;
590    }
591
592    sourceBytesPerRow = [sourceBitmap bytesPerRow];
593    numSourceRowsToSkip =
594                sourceBitmapFrame.size.height - (sourceRect.origin.y + sourceRect.size.height);
595    sourceDataOffset = numSourceRowsToSkip * sourceBytesPerRow
596                            + sizeof(PPImageBitmapPixel) * sourceRect.origin.x;
597    sourceRow = &sourceData[sourceDataOffset];
598
599    destinationBytesPerRow = [self bytesPerRow];
600    numDestinationRowsToSkip = destinationBitmapFrame.size.height
601                                - (destinationRect.origin.y + destinationRect.size.height);
602    destinationDataOffset = numDestinationRowsToSkip * destinationBytesPerRow
603                                + sizeof(PPImageBitmapPixel) * destinationRect.origin.x;
604    destinationRow = &destinationData[destinationDataOffset];
605
606    scaledRowDataSize = sourceRect.size.width * sizeof(PPImageBitmapPixel) * scalingFactor;
607    numTimesToCopyScaledRow = scalingFactor - 1;
608
609    pixelsPerRow = sourceRect.size.width;
610    rowCounter = sourceRect.size.height;
611
612    while (rowCounter--)
613    {
614        currentSourcePixel = (PPImageBitmapPixel *) sourceRow;
615        currentScaledPixel = (PPImageBitmapPixel *) destinationRow;
616
617        pixelCounter = pixelsPerRow;
618
619        while (pixelCounter--)
620        {
621            scaleCounter = scalingFactor;
622
623            while (scaleCounter--)
624            {
625                *currentScaledPixel++ = *currentSourcePixel;
626            }
627
628            currentSourcePixel++;
629        }
630
631        scaledRowData = destinationRow;
632        destinationRow += destinationBytesPerRow;
633
634        scaleCounter = numTimesToCopyScaledRow;
635
636        while (scaleCounter--)
637        {
638            memcpy(destinationRow, scaledRowData, scaledRowDataSize);
639
640            destinationRow += destinationBytesPerRow;
641        }
642
643        sourceRow += sourceBytesPerRow;
644    }
645
646    return;
647
648ERROR:
649    return;
650}
651
652- (void) ppScaledCopyFromImageBitmap: (NSBitmapImageRep *) sourceBitmap
653            inRect: (NSRect) sourceRect
654            toPoint: (NSPoint) destinationPoint
655            scalingFactor: (unsigned) scalingFactor
656            gridType: (PPGridType) gridType
657            gridPixelValue: (PPImageBitmapPixel) gridPixelValue
658{
659    NSRect destinationRect, sourceBitmapFrame, destinationBitmapFrame;
660    unsigned char *sourceData, *destinationData, *sourceRow, *destinationRow, *scaledRowData;
661    int scaledRowDataSize, sourceBytesPerRow, numSourceRowsToSkip, sourceDataOffset,
662            destinationBytesPerRow, numDestinationRowsToSkip, destinationDataOffset,
663            pixelsPerRow, rowCounter, pixelCounter, scaledPixelCounter, scaledRowCounter;
664    PPImageBitmapPixel *currentSourcePixel, *currentScaledPixel;
665
666    if (scalingFactor < kMinScalingFactorToDrawGrid)
667    {
668        if (scalingFactor == 1)
669        {
670            [self ppCopyFromBitmap: sourceBitmap
671                    inRect: sourceRect
672                    toPoint: destinationPoint];
673
674            return;
675        }
676        else
677        {
678            [self ppScaledCopyFromImageBitmap: sourceBitmap
679                    inRect: sourceRect
680                    toPoint: destinationPoint
681                    scalingFactor: scalingFactor];
682
683            return;
684        }
685    }
686
687    if (![self ppIsImageBitmap] || ![sourceBitmap ppIsImageBitmap])
688    {
689        goto ERROR;
690    }
691
692    if (scalingFactor <= kMaxScalingFactorToForceDotsGridType)
693    {
694        if (gridType != kPPGridType_Lines)
695        {
696            gridType = kPPGridType_Dots;
697        }
698    }
699
700    sourceRect = PPGeometry_PixelBoundsCoveredByRect(sourceRect);
701
702    if (NSIsEmptyRect(sourceRect))
703    {
704        goto ERROR;
705    }
706
707    destinationRect.origin = PPGeometry_PointClippedToIntegerValues(destinationPoint);
708    destinationRect.size = NSMakeSize(sourceRect.size.width * scalingFactor,
709                                        sourceRect.size.height * scalingFactor);
710
711    sourceBitmapFrame = [sourceBitmap ppFrameInPixels];
712    destinationBitmapFrame = [self ppFrameInPixels];
713
714    if (!NSContainsRect(sourceBitmapFrame, sourceRect)
715        || !NSContainsRect(destinationBitmapFrame, destinationRect))
716    {
717        goto ERROR;
718    }
719
720    sourceData = [sourceBitmap bitmapData];
721    destinationData = [self bitmapData];
722
723    if (!sourceData || !destinationData)
724    {
725        goto ERROR;
726    }
727
728    sourceBytesPerRow = [sourceBitmap bytesPerRow];
729    numSourceRowsToSkip =
730            sourceBitmapFrame.size.height - (sourceRect.origin.y + sourceRect.size.height);
731    sourceDataOffset = numSourceRowsToSkip * sourceBytesPerRow
732                        + sizeof(PPImageBitmapPixel) * sourceRect.origin.x;
733    sourceRow = &sourceData[sourceDataOffset];
734
735    destinationBytesPerRow = [self bytesPerRow];
736    numDestinationRowsToSkip = destinationBitmapFrame.size.height
737                                - (destinationRect.origin.y + destinationRect.size.height);
738    destinationDataOffset = numDestinationRowsToSkip * destinationBytesPerRow
739                                + sizeof(PPImageBitmapPixel) * destinationRect.origin.x;
740    destinationRow = &destinationData[destinationDataOffset];
741
742    pixelsPerRow = sourceRect.size.width;
743
744    scaledRowDataSize = pixelsPerRow * scalingFactor * sizeof(PPImageBitmapPixel);
745
746    switch (gridType)
747    {
748        case kPPGridType_Lines:     // GRIDTYPE: Lines
749        {
750            unsigned char *scaledHorizontalGridLineData;
751
752            // set up horizontal grid line
753
754            scaledHorizontalGridLineData = destinationRow;
755
756            currentScaledPixel = (PPImageBitmapPixel *) scaledHorizontalGridLineData;
757            scaledPixelCounter = pixelsPerRow * scalingFactor;
758
759            while (scaledPixelCounter--)
760            {
761                *currentScaledPixel++ = gridPixelValue;
762            }
763
764            // row loop
765
766            rowCounter = sourceRect.size.height;
767
768            while (rowCounter--)
769            {
770                // copy horizontal grid line
771
772                if (destinationRow != scaledHorizontalGridLineData)
773                {
774                    memcpy(destinationRow, scaledHorizontalGridLineData, scaledRowDataSize);
775                }
776
777                destinationRow += destinationBytesPerRow;
778
779                // set up scaled row data
780
781                scaledRowData = destinationRow;
782
783                currentSourcePixel = (PPImageBitmapPixel *) sourceRow;
784                currentScaledPixel = (PPImageBitmapPixel *) scaledRowData;
785
786                pixelCounter = pixelsPerRow;
787
788                while (pixelCounter--)
789                {
790                    // vertical grid line
791                    *currentScaledPixel++ = gridPixelValue;
792
793                    // scaled source-pixel data
794                    scaledPixelCounter = scalingFactor - 1;
795
796                    while (scaledPixelCounter--)
797                    {
798                        *currentScaledPixel++ = *currentSourcePixel;
799                    }
800
801                    currentSourcePixel++;
802                }
803
804                destinationRow += destinationBytesPerRow;
805
806                // copy scaled row data to remaining rows
807
808                scaledRowCounter = scalingFactor - 2;
809
810                while (scaledRowCounter--)
811                {
812                    memcpy(destinationRow, scaledRowData, scaledRowDataSize);
813                    destinationRow += destinationBytesPerRow;
814                }
815
816                sourceRow += sourceBytesPerRow;
817            }
818        }
819        break;
820
821        case kPPGridType_Dots:  // GRIDTYPE: Dots
822        {
823            rowCounter = sourceRect.size.height;
824
825            while (rowCounter--)
826            {
827                // set up scaled row data
828
829                scaledRowData = destinationRow;
830
831                currentSourcePixel = (PPImageBitmapPixel *) sourceRow;
832                currentScaledPixel = (PPImageBitmapPixel *) scaledRowData;
833
834                pixelCounter = pixelsPerRow;
835
836                while (pixelCounter--)
837                {
838                    // scaled source-pixel data (no grid pixels - dots are drawn at the end)
839
840                    scaledPixelCounter = scalingFactor;
841
842                    while (scaledPixelCounter--)
843                    {
844                        *currentScaledPixel++ = *currentSourcePixel;
845                    }
846
847                    currentSourcePixel++;
848                }
849
850                destinationRow += destinationBytesPerRow;
851
852                // copy scaled row data to remaining rows
853
854                scaledRowCounter = scalingFactor - 1;
855
856                while (scaledRowCounter--)
857                {
858                    memcpy(destinationRow, scaledRowData, scaledRowDataSize);
859                    destinationRow += destinationBytesPerRow;
860                }
861
862                // draw dots to first scaled row
863
864                currentScaledPixel = (PPImageBitmapPixel *) scaledRowData;
865
866                pixelCounter = pixelsPerRow;
867
868                while (pixelCounter--)
869                {
870                    *currentScaledPixel = gridPixelValue;
871                    currentScaledPixel += scalingFactor;
872                }
873
874                sourceRow += sourceBytesPerRow;
875            }
876        }
877        break;
878
879        default:    // GRIDTYPE: Crosshairs or Large Dots
880        {
881            unsigned gridLegLength, numScaledPixelsBetweenGridLegs;
882            unsigned char *scaledRowWithVerticalGridLegData;
883
884            if (gridType == kPPGridType_LargeDots)
885            {
886                // Large Dots
887                gridLegLength = 1;
888            }
889            else
890            {
891                // Crosshairs
892                gridLegLength = roundf(scalingFactor * kCrosshairLegSizeToScalingFactorRatio);
893            }
894
895            numScaledPixelsBetweenGridLegs = scalingFactor - 2 * gridLegLength - 1;
896
897            // row loop
898
899            rowCounter = sourceRect.size.height;
900
901            while (rowCounter--)
902            {
903                // set up scaled row data
904
905                scaledRowData = destinationRow;
906
907                currentSourcePixel = (PPImageBitmapPixel *) sourceRow;
908                currentScaledPixel = (PPImageBitmapPixel *) scaledRowData;
909
910                pixelCounter = pixelsPerRow;
911
912                while (pixelCounter--)
913                {
914                   // scaled source-pixel data (no grid pixels)
915
916                    scaledPixelCounter = scalingFactor;
917
918                    while (scaledPixelCounter--)
919                    {
920                        *currentScaledPixel++ = *currentSourcePixel;
921                    }
922
923                    currentSourcePixel++;
924                }
925
926                destinationRow += destinationBytesPerRow;
927
928                // set up scaled row with vertical grid-leg data (single left-edge grid-pixel)
929
930                scaledRowWithVerticalGridLegData = destinationRow;
931                memcpy(scaledRowWithVerticalGridLegData, scaledRowData, scaledRowDataSize);
932
933                currentScaledPixel = (PPImageBitmapPixel *) scaledRowWithVerticalGridLegData;
934
935                pixelCounter = pixelsPerRow;
936
937                while (pixelCounter--)
938                {
939                    *currentScaledPixel = gridPixelValue;
940                    currentScaledPixel += scalingFactor;
941                }
942
943                destinationRow += destinationBytesPerRow;
944
945                // copy scaled row with vertical grid-leg data to upper rows
946
947                scaledRowCounter = gridLegLength - 1;
948
949                while (scaledRowCounter--)
950                {
951                    memcpy(destinationRow, scaledRowWithVerticalGridLegData, scaledRowDataSize);
952                    destinationRow += destinationBytesPerRow;
953                }
954
955                // copy scaled row data between grid-leg rows
956
957                scaledRowCounter = numScaledPixelsBetweenGridLegs;
958
959                while (scaledRowCounter--)
960                {
961                    memcpy(destinationRow, scaledRowData, scaledRowDataSize);
962                    destinationRow += destinationBytesPerRow;
963                }
964
965                // copy scaled row with vertical grid-leg data to lower rows
966
967                scaledRowCounter = gridLegLength;
968
969                while (scaledRowCounter--)
970                {
971                    memcpy(destinationRow, scaledRowWithVerticalGridLegData, scaledRowDataSize);
972                    destinationRow += destinationBytesPerRow;
973                }
974
975                // draw horizontal grid-legs on first scaled row
976
977                currentScaledPixel = (PPImageBitmapPixel *) scaledRowData;
978
979                pixelCounter = pixelsPerRow;
980
981                while (pixelCounter--)
982                {
983                    // center pixel at intersection of horizontal & vertical grid legs
984                    *currentScaledPixel++ = gridPixelValue;
985
986                    scaledPixelCounter = gridLegLength;
987
988                    while (scaledPixelCounter--)
989                    {
990                        *currentScaledPixel++ = gridPixelValue;
991                    }
992
993                    currentScaledPixel += numScaledPixelsBetweenGridLegs;
994
995                    scaledPixelCounter = gridLegLength;
996
997                    while (scaledPixelCounter--)
998                    {
999                        *currentScaledPixel++ = gridPixelValue;
1000                    }
1001                }
1002
1003                sourceRow += sourceBytesPerRow;
1004            }
1005        }
1006        break;
1007    }
1008
1009    return;
1010
1011ERROR:
1012    return;
1013}
1014
1015- (NSBitmapImageRep *) ppImageBitmapWithMaxDimension: (float) maxDimension
1016{
1017    NSSize originalBitmapSize, maxSize, shrunkenBitmapSize;
1018    NSBitmapImageRep *shrunkenBitmap;
1019    NSImage *originalBitmapImage;
1020
1021    originalBitmapSize = [self ppSizeInPixels];
1022
1023    if (!PPGeometry_SizeExceedsDimension(originalBitmapSize, maxDimension))
1024    {
1025        return [self ppImageBitmap];
1026    }
1027
1028    maxSize = NSMakeSize(maxDimension, maxDimension);
1029
1030    shrunkenBitmapSize =
1031        PPGeometry_ScaledBoundsForFrameOfSizeToFitFrameOfSize(originalBitmapSize, maxSize).size;
1032
1033    shrunkenBitmap = [NSBitmapImageRep ppImageBitmapOfSize: shrunkenBitmapSize];
1034
1035    originalBitmapImage = [NSImage ppImageWithBitmap: self];
1036
1037    if (!shrunkenBitmap || !originalBitmapImage)
1038    {
1039        goto ERROR;
1040    }
1041
1042    [shrunkenBitmap ppSetAsCurrentGraphicsContext];
1043
1044    [[NSGraphicsContext currentContext] setImageInterpolation: NSImageInterpolationNone];
1045
1046    [originalBitmapImage drawInRect: PPGeometry_OriginRectOfSize(shrunkenBitmapSize)
1047                            fromRect: PPGeometry_OriginRectOfSize(originalBitmapSize)
1048                            operation: NSCompositeCopy
1049                            fraction: 1.0f];
1050
1051    [shrunkenBitmap ppRestoreGraphicsContext];
1052
1053    return shrunkenBitmap;
1054
1055ERROR:
1056    return nil;
1057}
1058
1059- (NSBitmapImageRep *) ppImageBitmapCompositedWithBackgroundColor: (NSColor *) backgroundColor
1060                        andBackgroundImage: (NSImage *) backgroundImage
1061                        backgroundImageInterpolation:
1062                                        (NSImageInterpolation) backgroundImageInterpolation
1063{
1064    NSRect bitmapFrame;
1065    NSBitmapImageRep *compositedBitmap;
1066    NSImage *bitmapImage;
1067
1068    if (!backgroundColor && !backgroundImage)
1069    {
1070        goto ERROR;
1071    }
1072
1073    bitmapFrame = [self ppFrameInPixels];
1074    compositedBitmap = [NSBitmapImageRep ppImageBitmapOfSize: bitmapFrame.size];
1075
1076    bitmapImage = [NSImage ppImageWithBitmap: self];
1077
1078    if (!compositedBitmap || !bitmapImage)
1079    {
1080        goto ERROR;
1081    }
1082
1083    [compositedBitmap ppSetAsCurrentGraphicsContext];
1084
1085    if (backgroundColor)
1086    {
1087        [backgroundColor set];
1088        NSRectFill(bitmapFrame);
1089    }
1090
1091    if (backgroundImage)
1092    {
1093        NSRect backgroundImageFrame, backgroundDestinationBounds;
1094
1095        backgroundImageFrame = PPGeometry_OriginRectOfSize([backgroundImage size]);
1096
1097        backgroundDestinationBounds =
1098            PPGeometry_ScaledBoundsForFrameOfSizeToFitFrameOfSize(backgroundImageFrame.size,
1099                                                                    bitmapFrame.size);
1100
1101        [[NSGraphicsContext currentContext]
1102                                        setImageInterpolation: backgroundImageInterpolation];
1103
1104        [backgroundImage drawInRect: backgroundDestinationBounds
1105                            fromRect: backgroundImageFrame
1106                            operation: NSCompositeSourceOver
1107                            fraction: 1.0f];
1108    }
1109
1110    [[NSGraphicsContext currentContext] setImageInterpolation: NSImageInterpolationNone];
1111
1112    [bitmapImage drawInRect: bitmapFrame
1113                    fromRect: bitmapFrame
1114                    operation: NSCompositeSourceOver
1115                    fraction: 1.0f];
1116
1117    [compositedBitmap ppRestoreGraphicsContext];
1118
1119    return compositedBitmap;
1120
1121ERROR:
1122    return self;
1123}
1124
1125- (NSBitmapImageRep *) ppImageBitmapDissolvedToOpacity: (float) opacity
1126{
1127    NSBitmapImageRep *dissolvedBitmap;
1128
1129    if (![self ppIsImageBitmap])
1130    {
1131        goto ERROR;
1132    }
1133
1134    if (opacity >= 1.0f)
1135    {
1136        dissolvedBitmap = [[self copy] autorelease];
1137
1138        if (!dissolvedBitmap)
1139            goto ERROR;
1140    }
1141    else
1142    {
1143        NSRect bitmapFrame = [self ppFrameInPixels];
1144
1145        dissolvedBitmap = [NSBitmapImageRep ppImageBitmapOfSize: bitmapFrame.size];
1146
1147        if (!dissolvedBitmap)
1148            goto ERROR;
1149
1150        if (opacity > 0.0f)
1151        {
1152            NSImage *image = [NSImage ppImageWithBitmap: self];
1153
1154            if (!image)
1155                goto ERROR;
1156
1157            [dissolvedBitmap ppSetAsCurrentGraphicsContext];
1158
1159            [image drawInRect: bitmapFrame
1160                    fromRect: bitmapFrame
1161                    operation: NSCompositeCopy
1162                    fraction: opacity];
1163
1164            [dissolvedBitmap ppRestoreGraphicsContext];
1165        }
1166    }
1167
1168    return dissolvedBitmap;
1169
1170ERROR:
1171    return nil;
1172}
1173
1174- (NSBitmapImageRep *) ppImageBitmapMaskedWithMask: (NSBitmapImageRep *) maskBitmap
1175{
1176    NSBitmapImageRep *maskedBitmap;
1177
1178    if (![self ppIsImageBitmapAndSameSizeAsMaskBitmap: maskBitmap])
1179    {
1180        goto ERROR;
1181    }
1182
1183    maskedBitmap = [NSBitmapImageRep ppImageBitmapOfSize: [self ppSizeInPixels]];
1184
1185    if (!maskedBitmap)
1186        goto ERROR;
1187
1188    [maskedBitmap ppMaskedCopyFromImageBitmap: self
1189                    usingMask: maskBitmap];
1190
1191    return maskedBitmap;
1192
1193ERROR:
1194    return nil;
1195}
1196
1197- (NSBitmapImageRep *) ppImageBitmapScaledByFactor: (unsigned) scalingFactor
1198                        shouldDrawGrid: (bool) shouldDrawGrid
1199                        gridType: (PPGridType) gridType
1200                        gridColor: (NSColor *) gridColor
1201{
1202    NSRect bitmapFrame;
1203    NSSize scaledBitmapSize;
1204    NSBitmapImageRep *scaledBitmap;
1205
1206    if (![self ppIsImageBitmap] || !scalingFactor)
1207    {
1208        goto ERROR;
1209    }
1210
1211    bitmapFrame = [self ppFrameInPixels];
1212
1213    scaledBitmapSize = NSMakeSize(bitmapFrame.size.width * scalingFactor,
1214                                    bitmapFrame.size.height * scalingFactor);
1215
1216    scaledBitmap = [NSBitmapImageRep ppImageBitmapOfSize: scaledBitmapSize];
1217
1218    if (!scaledBitmap)
1219        goto ERROR;
1220
1221    if (shouldDrawGrid)
1222    {
1223        [scaledBitmap ppScaledCopyFromImageBitmap: self
1224                        inRect: bitmapFrame
1225                        toPoint: NSZeroPoint
1226                        scalingFactor: scalingFactor
1227                        gridType: gridType
1228                        gridPixelValue: [gridColor ppImageBitmapPixelValue]];
1229    }
1230    else
1231    {
1232        [scaledBitmap ppScaledCopyFromImageBitmap: self
1233                        inRect: bitmapFrame
1234                        toPoint: NSZeroPoint
1235                        scalingFactor: scalingFactor];
1236    }
1237
1238    return scaledBitmap;
1239
1240ERROR:
1241    return nil;
1242}
1243
1244- (NSBitmapImageRep *) ppMaskBitmapForVisiblePixelsInImageBitmap
1245{
1246    NSSize bitmapSize;
1247    NSBitmapImageRep *maskBitmap;
1248
1249    if (![self ppIsImageBitmap])
1250    {
1251        goto ERROR;
1252    }
1253
1254    bitmapSize = [self ppSizeInPixels];
1255
1256    if (PPGeometry_IsZeroSize(bitmapSize))
1257    {
1258        goto ERROR;
1259    }
1260
1261    maskBitmap = [NSBitmapImageRep ppMaskBitmapOfSize: bitmapSize];
1262
1263    if (!maskBitmap)
1264        goto ERROR;
1265
1266    [maskBitmap ppMaskVisiblePixelsInImageBitmap: self selectionMask: nil];
1267
1268    return maskBitmap;
1269
1270ERROR:
1271    return nil;
1272}
1273
1274- (void) ppDrawImageGuidelinesInBounds: (NSRect) drawBounds
1275            topLeftPhase: (NSPoint) topLeftPhase
1276            unscaledSpacingSize: (NSSize) unscaledSpacingSize
1277            scalingFactor: (unsigned) scalingFactor
1278            guidelinePixelValue: (PPImageBitmapPixel) guidelinePixelValue
1279{
1280    NSRect bitmapFrame;
1281    int colOffsetToNextVerticalLine, rowOffsetToNextHorizontalLine, drawBoundsLeftCol,
1282        drawBoundsRightCol, drawBoundsTopRow, drawBoundsBottomRow, colOfFirstVerticalLine,
1283        numVerticalLines = 0, rowOfFirstHorizontalLine, numHorizontalLines = 0, bytesPerRow,
1284        startRow, rowIncrement, rowDataOffsetToFirstVerticalLine, dataOffset, rowDataIncrement,
1285        horizontalLineDataSize, rowOfNextHorizontalLine, row, verticalLineCounter;
1286    NSPoint offsetToNextGuidelineVertex;
1287    unsigned char *bitmapData, *previousHorizontalLineData = NULL, *rowData;
1288    PPImageBitmapPixel *verticalLinePixel;
1289
1290    if (![self ppIsImageBitmap])
1291    {
1292        goto ERROR;
1293    }
1294
1295    bitmapFrame = [self ppFrameInPixels];
1296
1297    drawBounds =
1298        NSIntersectionRect(PPGeometry_PixelBoundsCoveredByRect(drawBounds), bitmapFrame);
1299
1300    if (NSIsEmptyRect(drawBounds))
1301    {
1302        goto ERROR;
1303    }
1304
1305    topLeftPhase = PPGeometry_PointClippedToIntegerValues(topLeftPhase);
1306
1307    if (scalingFactor > kMaxCanvasZoomFactor)
1308    {
1309        goto ERROR;
1310    }
1311
1312    colOffsetToNextVerticalLine = unscaledSpacingSize.width * scalingFactor;
1313    rowOffsetToNextHorizontalLine = unscaledSpacingSize.height * scalingFactor;
1314
1315    if ((colOffsetToNextVerticalLine < kMinScalingFactorToDrawGrid)
1316        || (rowOffsetToNextHorizontalLine < kMinScalingFactorToDrawGrid))
1317    {
1318        // guidelines are too close together at current scalingFactor, so don't draw
1319        return;
1320    }
1321
1322    drawBoundsLeftCol = drawBounds.origin.x;
1323    drawBoundsRightCol = drawBoundsLeftCol + drawBounds.size.width - 1;
1324
1325    drawBoundsTopRow =
1326                bitmapFrame.size.height - (drawBounds.origin.y + drawBounds.size.height);
1327    drawBoundsBottomRow = drawBoundsTopRow + drawBounds.size.height - 1;
1328
1329    offsetToNextGuidelineVertex =
1330        PPGeometry_OffsetPointToNextNearestVertexOnGridWithSpacingSize(
1331                                                NSMakePoint(drawBoundsLeftCol + topLeftPhase.x,
1332                                                            drawBoundsTopRow + topLeftPhase.y),
1333                                                NSMakeSize(colOffsetToNextVerticalLine,
1334                                                            rowOffsetToNextHorizontalLine));
1335
1336    if ((offsetToNextGuidelineVertex.x < 0) || (offsetToNextGuidelineVertex.y < 0))
1337    {
1338        goto ERROR;
1339    }
1340
1341    colOfFirstVerticalLine = drawBoundsLeftCol + offsetToNextGuidelineVertex.x;
1342
1343    if (colOfFirstVerticalLine <= drawBoundsRightCol)
1344    {
1345        numVerticalLines = 1 + (drawBoundsRightCol - colOfFirstVerticalLine)
1346                                    / colOffsetToNextVerticalLine;
1347    }
1348
1349    rowOfFirstHorizontalLine = drawBoundsTopRow + offsetToNextGuidelineVertex.y;
1350
1351    if (rowOfFirstHorizontalLine <= drawBoundsBottomRow)
1352    {
1353        numHorizontalLines = 1 + (drawBoundsBottomRow - rowOfFirstHorizontalLine)
1354                                    / rowOffsetToNextHorizontalLine;
1355    }
1356
1357    if (!numVerticalLines && !numHorizontalLines)
1358    {
1359        // no guidelines within drawBounds - nothing to draw
1360        return;
1361    }
1362
1363    bitmapData = [self bitmapData];
1364    bytesPerRow = [self bytesPerRow];
1365
1366    if (!bitmapData || (bytesPerRow <= 0))
1367    {
1368        goto ERROR;
1369    }
1370
1371    if (numVerticalLines > 0)
1372    {
1373        startRow = drawBoundsTopRow;
1374        rowIncrement = 1;
1375
1376        rowDataOffsetToFirstVerticalLine =
1377                    (colOfFirstVerticalLine - drawBoundsLeftCol) * sizeof(PPImageBitmapPixel);
1378    }
1379    else
1380    {
1381        // no vertical guidelines within drawBounds - only need to draw horizontal guidelines
1382        startRow = rowOfFirstHorizontalLine;
1383        rowIncrement = rowOffsetToNextHorizontalLine;
1384
1385        rowDataOffsetToFirstVerticalLine = 0;   // avoids analyzer warning (undefined subscript)
1386    }
1387
1388    dataOffset = startRow * bytesPerRow + drawBoundsLeftCol * sizeof(PPImageBitmapPixel);
1389
1390    rowData = &bitmapData[dataOffset];
1391
1392    rowDataIncrement = rowIncrement * bytesPerRow;
1393
1394    horizontalLineDataSize = drawBounds.size.width * sizeof(PPImageBitmapPixel);
1395
1396    rowOfNextHorizontalLine = rowOfFirstHorizontalLine;
1397
1398    row = startRow;
1399
1400    while (row <= drawBoundsBottomRow)
1401    {
1402        if (row == rowOfNextHorizontalLine)
1403        {
1404            // row with horizontal line
1405
1406            if (!previousHorizontalLineData)
1407            {
1408                PPImageBitmapPixel *currentPixel = (PPImageBitmapPixel *) rowData;
1409                int pixelCounter = drawBounds.size.width;
1410
1411                while (pixelCounter--)
1412                {
1413                    *currentPixel++ = guidelinePixelValue;
1414                }
1415            }
1416            else
1417            {
1418                memcpy(rowData, previousHorizontalLineData, horizontalLineDataSize);
1419            }
1420
1421            previousHorizontalLineData = rowData;
1422
1423            rowOfNextHorizontalLine += rowOffsetToNextHorizontalLine;
1424        }
1425        else
1426        {
1427            // row with vertical lines
1428
1429            verticalLinePixel =
1430                        (PPImageBitmapPixel *) &rowData[rowDataOffsetToFirstVerticalLine];
1431
1432            verticalLineCounter = numVerticalLines;
1433
1434            while (verticalLineCounter--)
1435            {
1436                *verticalLinePixel = guidelinePixelValue;
1437
1438                verticalLinePixel += colOffsetToNextVerticalLine;
1439            }
1440        }
1441
1442        rowData += rowDataIncrement;
1443        row += rowIncrement;
1444    }
1445
1446    return;
1447
1448ERROR:
1449    return;
1450}
1451
1452@end
1453