سؤال

أحاول الوصول إلى البيانات الأولية لصورة نقطية بتنسيق ARGB_8888 على نظام Android، باستخدام ملف copyPixelsToBuffer و copyPixelsFromBuffer طُرق.ومع ذلك، يبدو أن استدعاء تلك الاستدعاءات يطبق دائمًا قناة ألفا على قنوات rgb.أحتاج إلى البيانات الأولية بالبايت [] أو ما شابه ذلك (للتمرير عبر JNI؛نعم، أعرف شيئًا عن bitmap.h في Android 2.2، ولا يمكنني استخدامه).

هنا عينة:

    // Create 1x1 Bitmap with alpha channel, 8 bits per channel
    Bitmap one = Bitmap.createBitmap(1,1,Bitmap.Config.ARGB_8888);
    one.setPixel(0,0,0xef234567);
    Log.v("?","hasAlpha() = "+Boolean.toString(one.hasAlpha()));
    Log.v("?","pixel before = "+Integer.toHexString(one.getPixel(0,0)));

    // Copy Bitmap to buffer
    byte[] store = new byte[4];
    ByteBuffer buffer  = ByteBuffer.wrap(store);
    one.copyPixelsToBuffer(buffer);

    // Change value of the pixel
    int value=buffer.getInt(0);
    Log.v("?", "value before = "+Integer.toHexString(value));
    value = (value >> 8) | 0xffffff00;
    buffer.putInt(0, value);
    value=buffer.getInt(0);
    Log.v("?", "value after = "+Integer.toHexString(value));

    // Copy buffer back to Bitmap
    buffer.position(0);
    one.copyPixelsFromBuffer(buffer);
    Log.v("?","pixel after = "+Integer.toHexString(one.getPixel(0,0)));

ثم يظهر السجل

hasAlpha() = true
pixel before = ef234567
value before = 214161ef
value after = ffffff61
pixel after = 619e9e9e

أدرك أن ترتيب قنوات argb مختلف؛هذا جيّد.لكنني لا أريد تطبيق قناة ألفا على كل نسخة (وهو ما يبدو أنه يفعله).

هل هذه هي الطريقة؟ copyPixelsToBuffer و copyPixelsFromBuffer من المفترض أن تعمل؟هل هناك أي طريقة للحصول على البيانات الأولية بالبايت[]؟

تمت إضافته ردًا على الإجابة أدناه:

وضع في buffer.order(ByteOrder.nativeOrder()); قبل copyPixelsToBuffer يغير النتيجة، ولكن لا يزال ليس بالطريقة التي أريدها:

pixel before = ef234567
value before = ef614121
value after = ffffff41
pixel after = ff41ffff

يبدو أنه يعاني من نفس المشكلة بشكل أساسي (يتم تطبيق ألفا على كل منهما copyPixelsFrom/ToBuffer).

هل كانت مفيدة؟

المحلول

أدرك أن هذا قديم جدًا وربما لن يساعدك الآن، لكنني صادفته مؤخرًا أثناء محاولتي الحصول عليه copyPixelsFromBuffer للعمل في تطبيقي.(شكرًا لك على طرح هذا السؤال، راجع للشغل!لقد وفرت لي الكثير من الوقت في تصحيح الأخطاء.) أقوم بإضافة هذه الإجابة على أمل أن تساعد الآخرين مثلي في المضي قدمًا ...

على الرغم من أنني لم أستخدم هذا بعد للتأكد من أنه يعمل، يبدو أنه اعتبارًا من مستوى واجهة برمجة التطبيقات (API) 19، سيكون لدينا أخيرًا طريقة لتحديد عدم "تطبيق ألفا" (المعروف أيضًا باسم.تتضاعف) داخل Bitmap.إنهم يضيفون أ setPremultiplied(boolean) الطريقة التي من شأنها أن تساعد في مثل هذه المواقف من خلال السماح لنا بالتحديد false.

آمل أن يساعد هذا!

نصائح أخرى

إحدى طرق الوصول إلى البيانات في الصورة النقطية هي استخدام طريقة getPixels().يمكنك العثور أدناه على مثال استخدمته للحصول على صورة ذات تدرج رمادي من بيانات argb ثم العودة من مصفوفة البايت إلى الصورة النقطية (بالطبع إذا كنت بحاجة إلى rgb، يمكنك حجز 3x بايت وحفظها كلها...):

/*Free to use licence by Sami Varjo (but nice if you retain this line)*/

public final class BitmapConverter {

    private BitmapConverter(){};

   /**
    * Get grayscale data from argb image to byte array
    */
   public static byte[] ARGB2Gray(Bitmap img)
   {

       int width = img.getWidth();
       int height = img.getHeight();

       int[] pixels = new int[height*width];
       byte grayIm[] = new byte[height*width];

       img.getPixels(pixels,0,width,0,0,width,height);

       int pixel=0;
       int count=width*height;

       while(count-->0){
           int inVal = pixels[pixel];

           //Get the pixel channel values from int 
           double r = (double)( (inVal & 0x00ff0000)>>16 );
           double g = (double)( (inVal & 0x0000ff00)>>8  );
           double b = (double)(  inVal & 0x000000ff)      ;

           grayIm[pixel++] = (byte)( 0.2989*r + 0.5870*g + 0.1140*b );
       }

       return grayIm;
   }

   /**
    * Create a gray scale bitmap from byte array
    */
   public static Bitmap gray2ARGB(byte[] data, int width, int height)
   {
       int count = height*width;
       int[] outPix = new int[count];
       int pixel=0;
       while(count-->0){
           int val = data[pixel] & 0xff; //convert byte to unsigned
           outPix[pixel++] = 0xff000000 | val << 16 | val << 8 | val ;
       }

       Bitmap out =  Bitmap.createBitmap(outPix,0,width,width, height, Bitmap.Config.ARGB_8888);
       return out;
   }

}

أعتقد أن هذا قد يكون له علاقة بترتيب البايت الخاص بـ ByteBuffer الذي تستخدمه.يستخدم ByteBuffer endian الكبير بشكل افتراضي.اضبط endianness على المخزن المؤقت باستخدام

buffer.order(ByteOrder.nativeOrder());

معرفة ما إذا كان يساعد.

علاوة على ذلك، فإن CopyPixelsFromBuffer/copyPixelsToBuffer لا يغير بيانات البكسل بأي شكل من الأشكال.يتم نسخها الخام.

هذا سؤال قديم، لكنني وصلت إلى نفس المشكلة، واكتشفت للتو أن بايت الصورة النقطية يتم مضاعفةها مسبقًا، ويمكنك ضبط الصورة النقطية (اعتبارًا من API 19) على عدم مضاعفة المخزن المؤقت مسبقًا، ولكن في واجهة برمجة التطبيقات لا يقدمون أي ضمان.

من المستندات:

public final void setPremultiplied(boolean premultiplied)

يضبط ما إذا كان يجب على الصورة النقطية التعامل مع بياناتها على أنها مضروبة مسبقًا.يتم دائمًا التعامل مع الصور النقطية على أنها مضروبة مسبقًا في نظام العرض ولوحة Canvas لأسباب تتعلق بالأداء.تخزين البيانات غير المتضاعفة مسبقًا في صورة نقطية (من خلال setPixel, setPixels, ، أو BitmapFactory.Options.inPremultiplied) يمكن أن يؤدي إلى مزج غير صحيح إذا تم رسمه بواسطة الإطار.

لن تؤثر هذه الطريقة على سلوك الصورة النقطية بدون قناة ألفا، أو إذا hasAlpha() يعود كاذبة.

الاتصال createBitmap أو createScaledBitmap مع الصورة النقطية المصدر التي لم يتم مضاعفة ألوانها مسبقًا، قد يؤدي ذلك إلى حدوث RuntimeException, ، نظرًا لأن هذه الوظائف تتطلب رسم المصدر، وهو غير مدعوم للصور النقطية غير المتضاعفة مسبقًا.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top