UIImage.CGImage पास होने पर CGContextDrawImage छवि को ऊपर की ओर खींचता है


179

क्या किसी को पता है कि CGContextDrawImageमेरी छवि को उल्टा क्यों बनाया जाएगा? मैं अपने एप्लिकेशन से एक छवि लोड कर रहा हूं:

UIImage *image = [UIImage imageNamed:@"testImage.png"];

और फिर बस कोर ग्राफिक्स को मेरे संदर्भ में खींचने के लिए कह रहा हूं:

CGContextDrawImage(context, CGRectMake(0, 0, 145, 15), image.CGImage);

यह सही जगह और आयामों में प्रदान करता है, लेकिन छवि उल्टा है। मुझे यहाँ कुछ स्पष्ट याद आ रहा होगा?

जवाबों:


238

के बजाय

CGContextDrawImage(context, CGRectMake(0, 0, 145, 15), image.CGImage);

उपयोग

[image drawInRect:CGRectMake(0, 0, 145, 15)];

अपनी शुरुआत / अंत के CGcontextतरीकों के बीच में ।

यह आपकी वर्तमान छवि के संदर्भ में सही अभिविन्यास के साथ छवि को आकर्षित करेगा - मुझे पूरा यकीन है कि यह UIImageअभिविन्यास के ज्ञान पर पकड़ के साथ कुछ करना है, जबकि CGContextDrawImageविधि अंतर्निहित कच्ची छवि डेटा प्राप्त करती है जिसमें अभिविन्यास की समझ नहीं है।


19
यह समाधान आपको अपनी छवि पर CG फ़ंक्शन का उपयोग करने की अनुमति नहीं देता है, जैसे CGContextSetAlpha (), जबकि दूसरा उत्तर देता है।
samvermette

2
अच्छी बात है, हालांकि ज्यादातर आपको छवि बनाने के लिए कस्टम अल्फा स्तरों की आवश्यकता नहीं होती है (आमतौर पर उन चीजों के लिए समय से पहले छवियों में बेक किया जाता है जिन्हें अल्फा की आवश्यकता होती है)। मूल रूप से मेरा आदर्श वाक्य है, थोड़ा कोड का उपयोग करें क्योंकि आप अधिक कोड का अर्थ है कि कीड़े के लिए अधिक संभावनाएं हो सकती हैं।
केंडल हेल्मसटेटर गेलनर

नमस्ते, आपके शुरुआती / अंत CGContext विधियों के बीच में क्या मतलब है, क्या आप मुझे अधिक नमूना कोड दे सकते हैं। मैं संदर्भ को कहीं और संग्रहीत करने की कोशिश कर रहा हूं और संदर्भ का उपयोग करने के लिए छवि को
खींचता हूं

2
हालांकि यह जंग खाए काम कर सकता है, - [UIImage drawInRect:] की समस्या है कि यह थ्रेड-सुरक्षित नहीं है। मेरे विशेष आवेदन के लिए, इसीलिए मैं पहली बार CGContextDrawImage () का उपयोग कर रहा हूं।
ओली

2
बस स्पष्ट करने के लिए: कोर ग्राफिक्स ओएस एक्स पर बनाया गया है जहां आईओएस की तुलना में समन्वय प्रणाली उल्टा है (वाई ओएस एक्स में निचले बाएं हाथ की तरफ शुरू होती है)। यही कारण है कि कोर ग्राफिक्स छवि को उल्टा खींचता है जबकि drawInRect आपके लिए इसे संभालता है
borchero

213

यहां तक ​​कि मैंने जो कुछ भी उल्लेख किया है, उसे लागू करने के बाद भी, मैंने अभी भी चित्रों के साथ नाटक किया है। अंत में, मैंने सिर्फ अपनी सभी छवियों का 'फ़्लिप वर्टिकल' संस्करण बनाने के लिए जिम्प का उपयोग किया है। अब मुझे किसी भी ट्रांसफॉर्म का उपयोग करने की आवश्यकता नहीं है। उम्मीद है कि इससे ट्रैक पर और समस्या नहीं होगी।

क्या कोई जानता है कि CGContextDrawImage मेरी छवि को उल्टा क्यों बना रहा होगा? मैं अपने एप्लिकेशन से एक छवि लोड कर रहा हूं:

क्वार्ट्ज 2 डी एक अलग समन्वय प्रणाली का उपयोग करता है, जहां मूल निचले बाएं कोने में है। इसलिए जब क्वार्ट्ज पिक्सेल x [5], y [10] को 100 * 100 की छवि के साथ खींचता है, तो उस पिक्सेल को ऊपरी बाएँ के बजाय निचले बाएँ कोने में खींचा जा रहा है। इस प्रकार 'फ़्लिप' छवि का कारण बना।

एक्स को-ऑर्डिनेट सिस्टम मैच करता है, इसलिए आपको y को-ऑर्डिनेट्स को फ्लिप करना होगा।

CGContextTranslateCTM(context, 0, image.size.height);

इसका मतलब है कि हमने एक्स अक्ष पर 0 इकाइयों द्वारा और y अक्ष पर छवियों की ऊंचाई से छवि का अनुवाद किया है। हालाँकि, इसका अर्थ यह होगा कि हमारी छवि अभी भी उलटी है, बस "image.size.height" को नीचे खींचा जा रहा है जहाँ हम चाहते हैं कि इसे खींचा जाए।

Quartz2D प्रोग्रामिंग गाइड छवि को फ्लिप करने के लिए ScaleCTM का उपयोग करने और नकारात्मक मान पारित करने की सिफारिश करता है। ऐसा करने के लिए आप निम्न कोड का उपयोग कर सकते हैं -

CGContextScaleCTM(context, 1.0, -1.0);

अपने CGContextDrawImageकॉल से ठीक पहले दोनों को मिलाएं और आपके पास छवि सही ढंग से आनी चाहिए।

UIImage *image = [UIImage imageNamed:@"testImage.png"];    
CGRect imageRect = CGRectMake(0, 0, image.size.width, image.size.height);       

CGContextTranslateCTM(context, 0, image.size.height);
CGContextScaleCTM(context, 1.0, -1.0);

CGContextDrawImage(context, imageRect, image.CGImage);

बस सावधान रहें यदि आपका छवि-निर्देशांक आपकी छवि से मेल नहीं खाता है, क्योंकि आप अनपेक्षित परिणाम प्राप्त कर सकते हैं।

निर्देशांक वापस बदलने के लिए:

CGContextScaleCTM(context, 1.0, -1.0);
CGContextTranslateCTM(context, 0, -imageRect.size.height);

यह अच्छा है लेकिन मैंने पाया कि जब मैं 0,0 और 1.0,1.0 पर वापस जाने की कोशिश कर रहा था तब भी अन्य सामान अनुवाद और पैमाने से प्रभावित हो रहा था, मैं अंत में केंडल के समाधान के साथ गया था, लेकिन मुझे लगता है कि यह UIImage के बजाय का उपयोग कर रहा है निचले स्तर CGImage सामान जो हम यहां काम कर रहे हैं
मूँगफली के दाने

3
यदि आप drawLayer का उपयोग कर रहे हैं और संदर्भ में CGImageRef आकर्षित करना चाहते हैं, तो यह तकनीक यहां केवल डॉक्टर द्वारा ऑर्डर किए जाने के लिए है! यदि छवि के लिए आयत है, तो CGContextTranslateCTM (संदर्भ, 0, image.size.height + image.origin.y), इसके बाद CG.ontextDrawImage () से पहले rect.origin.y को 0 पर सेट करें। धन्यवाद!
डेविड एच

मैं एक बार ImageMagick जहां iPhone कैमरा गलत अभिविन्यास में परिणाम के साथ समस्या थी। यह पता चला है कि यह छवि फ़ाइल में अभिविन्यास ध्वज है, इसलिए चित्र छवियां थीं, 960px x 640px ध्वज को "बाईं ओर" सेट करें - जैसा कि बाईं ओर शीर्ष (या उसके करीब कुछ) है। यह धागा मदद कर सकता है: stackoverflow.com/questions/1260249/…
हरि करम सिंह

8
आप का उपयोग नहीं करते क्यों CGContextSaveGState()और CGContextRestoreGState()बचाने के लिए और परिवर्तन मैट्रिक्स बहाल करने के लिए?
जाक

20

दोनों दुनिया के सर्वश्रेष्ठ, UIImage का उपयोग करें drawAtPoint:या drawInRect:अभी भी अपने कस्टम संदर्भ को निर्दिष्ट करते हुए:

UIGraphicsPushContext(context);
[image drawAtPoint:CGPointZero]; // UIImage will handle all especial cases!
UIGraphicsPopContext();

इसके अलावा आप अपने संदर्भ को संशोधित करने से बचते हैं CGContextTranslateCTMया CGContextScaleCTMजो दूसरा उत्तर देता है।


यह एक महान विचार है, लेकिन मेरे लिए यह एक ऐसी छवि का परिणाम है जो उल्टा था और दाएं से बाएं था। मैं अनुमान लगा रहा हूं कि परिणाम छवि से छवि में भिन्न होंगे।
ल्यूक रोजर्स

शायद बाद में आईओएस रिलीज के साथ काम करना बंद कर दिया? उस समय इसने मेरे लिए हर चीज के साथ काम किया।
रिवेरा

मुझे लगता है कि यह वास्तव में नीचे था कि मैं संदर्भ कैसे बना रहा हूं। एक बार जब मैंने UIGraphicsBeginImageContextWithOptionsकेवल एक नई पारी शुरू करने के बजाय इसका उपयोग करना शुरू किया CGContext, तो यह काम करने लगा।
ल्यूक रोजर्स

12

प्रासंगिक क्वार्ट्ज 2 डी डॉक्स: https://developer.apple.com/library/ios/documentation/2DDrawing/Conceptual/DrawingPrintingiOS/GraphicsDrawingOverview/GraphicsDrawingOverview.html#//apple_ref/doc/uid/TP4001/00101/0010156/56

डिफ़ॉल्ट समन्वय प्रणाली को फ़्लिप करना

यूइकिट ड्राइंग में फ़्लिप करना एक आकर्षक वातावरण को संरेखित करने के लिए बैकिंग क्लेयर को संशोधित करता है, जिसमें यूआईओआईटी के डिफ़ॉल्ट समन्वय प्रणाली के साथ एक एलएलओ समन्वय प्रणाली है। यदि आप ड्राइंग के लिए केवल UIKit विधियों और फ़ंक्शन का उपयोग करते हैं, तो आपको CTM को फ्लिप करने की आवश्यकता नहीं होनी चाहिए। हालाँकि, यदि आप UIKit कॉल के साथ कोर ग्राफिक्स या इमेज I / O फ़ंक्शन कॉल को मिलाते हैं, तो CTM को फ़्लिप करना आवश्यक हो सकता है।

विशेष रूप से, यदि आप सीधे कोर ग्राफिक्स फ़ंक्शन को कॉल करके एक छवि या पीडीएफ दस्तावेज़ खींचते हैं, तो वस्तु को दृश्य के संदर्भ में उल्टा प्रस्तुत किया जाता है। छवि और पृष्ठों को सही ढंग से प्रदर्शित करने के लिए आपको CTM को फ्लिप करना होगा।

एक कोर ग्राफिक्स संदर्भ के लिए खींची गई वस्तु को फ्लिप करने के लिए ताकि यह UIKit दृश्य में प्रदर्शित होने पर सही ढंग से दिखाई दे, आपको CTM को दो चरणों में संशोधित करना होगा। आप ड्राइंग क्षेत्र के ऊपरी-बाएँ कोने में मूल का अनुवाद करते हैं, और फिर आप एक स्केल ट्रांसलेशन लागू करते हैं, y-निर्देशांक -1 को संशोधित करते हैं। ऐसा करने के लिए कोड निम्न के जैसा दिखता है:

CGContextSaveGState(graphicsContext);
CGContextTranslateCTM(graphicsContext, 0.0, imageHeight);
CGContextScaleCTM(graphicsContext, 1.0, -1.0);
CGContextDrawImage(graphicsContext, image, CGRectMake(0, 0, imageWidth, imageHeight));
CGContextRestoreGState(graphicsContext);

10

यदि कोई किसी संदर्भ में कस्टम रेक्ट में छवि बनाने के लिए नो-ब्रेनर समाधान में रुचि रखता है:

 func drawImage(image: UIImage, inRect rect: CGRect, context: CGContext!) {

    //flip coords
    let ty: CGFloat = (rect.origin.y + rect.size.height)
    CGContextTranslateCTM(context, 0, ty)
    CGContextScaleCTM(context, 1.0, -1.0)

    //draw image
    let rect__y_zero = CGRect(origin: CGPoint(x: rect.origin.x, y:0), size: rect.size)
    CGContextDrawImage(context, rect__y_zero, image.CGImage)

    //flip back
    CGContextScaleCTM(context, 1.0, -1.0)
    CGContextTranslateCTM(context, 0, -ty)

}

रेक्ट भरने के लिए इमेज को स्केल किया जाएगा।


7

मुझे यकीन नहीं है UIImage, लेकिन इस तरह का व्यवहार आमतौर पर तब होता है जब निर्देशांक फ़्लिप किया जाता है। ज्यादातर OS X निर्देशांक सिस्टम की निचले बाएँ कोने में अपनी उत्पत्ति है, जैसा कि पोस्टस्क्रिप्ट और पीडीएफ में है। लेकिन CGImageऊपरी बाएं कोने में समन्वय प्रणाली की उत्पत्ति है।

संभावित समाधानों में एक isFlippedसंपत्ति या एक scaleYBy:-1परिशोधन परिवर्तन शामिल हो सकता है ।


3

UIImageइसमें CGImageमुख्य सामग्री सदस्य के साथ-साथ स्केलिंग और ओरिएंटेशन कारक शामिल हैं। चूंकि CGImageऔर इसके विभिन्न कार्य OSX से प्राप्त होते हैं, यह एक समन्वय प्रणाली की अपेक्षा करता है जो iPhone की तुलना में उल्टा है। जब आप एक बनाते हैं UIImage, तो यह क्षतिपूर्ति करने के लिए एक उल्टा-उन्मुख अभिविन्यास में चूक करता है (आप इसे बदल सकते हैं!)। उपयोग । CGImageबहुत शक्तिशाली CGImageकार्यों का उपयोग करने के लिए संपत्ति , लेकिन iPhone स्क्रीन पर ड्राइंग आदि UIImageविधियों के साथ सबसे अच्छा किया जाता है ।


1
आप UIImage का डिफ़ॉल्ट अप-डाउन ओरिएंटेशन कैसे बदलते हैं?
मेगामैनएक्स

3

स्विफ्ट कोड के साथ पूरक उत्तर

क्वार्ट्ज 2 डी ग्राफिक्स नीचे बाईं ओर मूल के साथ एक समन्वय प्रणाली का उपयोग करता है जबकि आईओएस में यूआईआईआईटी शीर्ष बाईं ओर मूल के साथ एक समन्वय प्रणाली का उपयोग करता है। सब कुछ आमतौर पर ठीक काम करता है लेकिन कुछ ग्राफिक्स ऑपरेशन करते समय, आपको समन्वय प्रणाली को स्वयं संशोधित करना होगा। प्रलेखन कहता है:

कुछ तकनीकों ने अपने ग्राफिक्स संदर्भों को क्वार्ट्ज द्वारा उपयोग किए जाने वाले एक अलग डिफ़ॉल्ट समन्वय प्रणाली का उपयोग करके स्थापित किया। क्वार्ट्ज के सापेक्ष, इस तरह की एक समन्वय प्रणाली एक संशोधित समन्वय प्रणाली है और कुछ क्वार्ट्ज ड्राइंग ऑपरेशन करते समय इसकी भरपाई की जानी चाहिए। सबसे आम संशोधित समन्वित प्रणाली मूल को संदर्भ के ऊपरी-बाएँ कोने में रखती है और पृष्ठ के निचले भाग की ओर इंगित करने के लिए y- अक्ष को परिवर्तित करती है।

इस घटना को कस्टम विचारों के निम्नलिखित दो उदाहरणों में देखा जा सकता है जो उनके drawRectतरीकों में एक छवि बनाते हैं ।

यहां छवि विवरण दर्ज करें

बाईं ओर, छवि उल्टा-नीचे है और दाईं ओर समन्वय प्रणाली का अनुवाद और माप किया गया है ताकि मूल शीर्ष बाईं ओर हो।

उल्टा छवि

override func drawRect(rect: CGRect) {

    // image
    let image = UIImage(named: "rocket")!
    let imageRect = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height)

    // context
    let context = UIGraphicsGetCurrentContext()

    // draw image in context
    CGContextDrawImage(context, imageRect, image.CGImage)

}

संशोधित समन्वय प्रणाली

override func drawRect(rect: CGRect) {

    // image
    let image = UIImage(named: "rocket")!
    let imageRect = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height)

    // context
    let context = UIGraphicsGetCurrentContext()

    // save the context so that it can be undone later
    CGContextSaveGState(context)

    // put the origin of the coordinate system at the top left
    CGContextTranslateCTM(context, 0, image.size.height)
    CGContextScaleCTM(context, 1.0, -1.0)

    // draw the image in the context
    CGContextDrawImage(context, imageRect, image.CGImage)

    // undo changes to the context
    CGContextRestoreGState(context)
}


2

हम एक ही फ़ंक्शन का उपयोग करके इस समस्या को हल कर सकते हैं:

UIGraphicsBeginImageContext(image.size);

UIGraphicsPushContext(context);

[image drawInRect:CGRectMake(gestureEndPoint.x,gestureEndPoint.y,350,92)];

UIGraphicsPopContext();

UIGraphicsEndImageContext();

1

drawInRectनिश्चित रूप से जाने का रास्ता है। यहां एक और छोटी बात है जो ऐसा करते समय उपयोगी होगी। आमतौर पर चित्र और आयत जिसमें यह जा रहा है वह अनुरूप नहीं है। उस स्थिति drawInRectमें तस्वीर खिंच जाएगी। यहां यह सुनिश्चित करने का एक त्वरित और अच्छा तरीका है कि तस्वीर के पहलू राशन को बदल नहीं दिया जाता है, परिवर्तन को उलट कर (जिसमें पूरी बात फिट होगी):

//Picture and irect don't conform, so there'll be stretching, compensate
    float xf = Picture.size.width/irect.size.width;
    float yf = Picture.size.height/irect.size.height;
    float m = MIN(xf, yf);
    xf /= m;
    yf /= m;
    CGContextScaleCTM(ctx, xf, yf);

    [Picture drawInRect: irect];

1

स्विफ्ट 3 कोरग्राफिक्स सॉल्यूशन

यदि आप UIImage के बजाय जो भी आपके कारण हो सकता है, उसके लिए CG का उपयोग करना चाहते हैं, तो पिछले उत्तरों पर आधारित इस Swift 3 निर्माण ने मेरे लिए समस्या को हल किया:

if let cgImage = uiImage.cgImage {
    cgContext.saveGState()
    cgContext.translateBy(x: 0.0, y: cgRect.size.height)
    cgContext.scaleBy(x: 1.0, y: -1.0)
    cgContext.draw(cgImage, in: cgRect)
    cgContext.restoreGState()
}

1

ऐसा इसलिए होता है क्योंकि क्वार्ट्जकोर में समन्वय प्रणाली "बॉटम-लेफ्ट" होती है, जबकि यूइकिट - "टॉप-लेफ्ट"।

मामले में आप CGContext का विस्तार कर सकते हैं :

extension CGContext {
  func changeToTopLeftCoordinateSystem() {
    translateBy(x: 0, y: boundingBoxOfClipPath.size.height)
    scaleBy(x: 1, y: -1)
  }
}

// somewhere in render 
ctx.saveGState()
ctx.changeToTopLeftCoordinateSystem()
ctx.draw(cgImage!, in: frame)

1

मैं इस स्विफ्ट 5 , शुद्ध कोर ग्राफिक्स एक्सटेंशन का उपयोग करता हूं , जो छवि के अवशेषों में गैर-शून्य उत्पत्ति को सही ढंग से संभालता है:

extension CGContext {

    /// Draw `image` flipped vertically, positioned and scaled inside `rect`.
    public func drawFlipped(_ image: CGImage, in rect: CGRect) {
        self.saveGState()
        self.translateBy(x: 0, y: rect.origin.y + rect.height)
        self.scaleBy(x: 1.0, y: -1.0)
        self.draw(image, in: CGRect(origin: CGPoint(x: rect.origin.x, y: 0), size: rect.size))
        self.restoreGState()
    }
}

आप इसे बिल्कुल CGContextनियमित draw(: in:)विधि की तरह उपयोग कर सकते हैं :

ctx.drawFlipped(myImage, in: myRect)

0

अपने प्रोजेक्ट के दौरान मैंने फोन से लोड की गई छवियों के लिए इस समस्या को हल करने के लिए क्लिफ के जवाब में केंडल के उत्तर से छलांग लगा दी।

अंत में मैं CGImageCreateWithPNGDataProviderइसके बजाय का उपयोग कर समाप्त हो गया :

NSString* imageFileName = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"clockdial.png"];

return CGImageCreateWithPNGDataProvider(CGDataProviderCreateWithFilename([imageFileName UTF8String]), NULL, YES, kCGRenderingIntentDefault);

यह उन अभिविन्यास मुद्दों से ग्रस्त नहीं है जो आपको CGImageएक से प्राप्त करने से प्राप्त होंगे UIImageऔर इसे CALayerएक अड़चन के बिना सामग्री के रूप में उपयोग किया जा सकता है।


0
func renderImage(size: CGSize) -> UIImage {
    return UIGraphicsImageRenderer(size: size).image { rendererContext in
        // flip y axis
        rendererContext.cgContext.translateBy(x: 0, y: size.height)
        rendererContext.cgContext.scaleBy(x: 1, y: -1)

        // draw image rotated/offsetted
        rendererContext.cgContext.saveGState()
        rendererContext.cgContext.translateBy(x: translate.x, y: translate.y)
        rendererContext.cgContext.rotate(by: rotateRadians)
        rendererContext.cgContext.draw(cgImage, in: drawRect)
        rendererContext.cgContext.restoreGState()
    }
}

0

@ ZpaceZombor के उत्कृष्ट उत्तर के आधार पर स्विफ्ट 5 उत्तर

यदि आपके पास UIImage है, तो उपयोग करें

var image: UIImage = .... 
image.draw(in: CGRect)

यदि आपके पास CGImage है तो नीचे मेरी श्रेणी का उपयोग करें

नोट: कुछ अन्य उत्तरों के विपरीत, यह ध्यान में रखता है कि जो आयत आप खींचना चाहते हैं, उसमें y! = 0. वे उत्तर हैं जो इस बात को ध्यान में नहीं रखते कि वे गलत हैं और सामान्य स्थिति में काम नहीं करेंगे।

extension CGContext {
    final func drawImage(image: CGImage, inRect rect: CGRect) {

        //flip coords
        let ty: CGFloat = (rect.origin.y + rect.size.height)
        translateBy(x: 0, y: ty)
        scaleBy(x: 1.0, y: -1.0)

        //draw image
        let rect__y_zero = CGRect(x: rect.origin.x, y: 0, width: rect.width, height: rect.height)
        draw(image, in: rect__y_zero)

        //flip back
        scaleBy(x: 1.0, y: -1.0)
        translateBy(x: 0, y: -ty)

    }
} 

इस तरह का उपयोग करें:

let imageFrame: CGRect = ...
let context: CGContext = ....
let img: CGImage = ..... 
context.drawImage(image: img, inRect: imageFrame)

यह लगभग एक सही समाधान है, लेकिन विचार करने के लिए फ़ंक्शन को बदलने पर विचार करें (छवि: UIImage, inRect rect: CGRect) और uiImage.cgimage को विधि के अंदर संभालें
मंगल

-5

आप इस समस्या को हल कर सकते हैं:

//Using an Image as a mask by directly inserting UIImageObject.CGImage causes
//the same inverted display problem. This is solved by saving it to a CGImageRef first.

//CGImageRef image = [UImageObject CGImage];

//CGContextDrawImage(context, boundsRect, image);

Nevermind... Stupid caching.
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.