aboutsummaryrefslogtreecommitdiff
path: root/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeBitmapFactory.java
blob: 54213d87cd2d732328567b424514c1912e110d3e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
package org.robolectric.shadows;

import static android.os.Build.VERSION_CODES.LOLLIPOP;
import static android.os.Build.VERSION_CODES.O;
import static android.os.Build.VERSION_CODES.P;
import static android.os.Build.VERSION_CODES.Q;
import static org.robolectric.util.reflector.Reflector.reflector;

import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapFactory.Options;
import android.graphics.Rect;
import java.io.FileDescriptor;
import java.io.InputStream;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.nativeruntime.BitmapFactoryNatives;
import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowNativeBitmapFactory.Picker;
import org.robolectric.util.reflector.Direct;
import org.robolectric.util.reflector.ForType;

/** Shadow for {@link BitmapFactory} that is backed by native code */
@Implements(
    value = BitmapFactory.class,
    minSdk = O,
    shadowPicker = Picker.class,
    isInAndroidSdk = false)
public class ShadowNativeBitmapFactory {

  static {
    DefaultNativeRuntimeLoader.injectAndLoad();
  }

  @Implementation
  protected static Bitmap decodeResource(Resources res, int id, BitmapFactory.Options options) {
    Bitmap bitmap = reflector(BitmapFactoryReflector.class).decodeResource(res, id, options);
    if (bitmap == null) {
      return null;
    }

    ShadowNativeBitmap shadowNativeBitmap = Shadow.extract(bitmap);
    shadowNativeBitmap.setCreatedFromResId(id);
    return bitmap;
  }

  @Implementation
  protected static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts) {
    reflector(BitmapFactoryOptionsReflector.class).validate(opts);
    Bitmap bitmap =
        reflector(BitmapFactoryReflector.class).decodeStreamInternal(is, outPadding, opts);
    reflector(BitmapFactoryReflector.class).setDensityFromOptions(bitmap, opts);
    return bitmap;
  }

  @Implementation(minSdk = Q)
  protected static Bitmap nativeDecodeStream(
      InputStream is,
      byte[] storage,
      Rect padding,
      Options opts,
      long inBitmapHandle,
      long colorSpaceHandle) {
    return BitmapFactoryNatives.nativeDecodeStream(
        is, storage, padding, opts, inBitmapHandle, colorSpaceHandle);
  }

  @Implementation(maxSdk = P)
  protected static Bitmap nativeDecodeStream(
      InputStream is, byte[] storage, Rect padding, Options opts) {
    return nativeDecodeStream(is, storage, padding, opts, nativeInBitmap(opts), 0);
  }

  @Implementation(minSdk = Q)
  protected static Bitmap nativeDecodeFileDescriptor(
      FileDescriptor fd, Rect padding, Options opts, long inBitmapHandle, long colorSpaceHandle) {
    return BitmapFactoryNatives.nativeDecodeFileDescriptor(
        fd, padding, opts, inBitmapHandle, colorSpaceHandle);
  }

  @Implementation(maxSdk = P)
  protected static Bitmap nativeDecodeFileDescriptor(
      FileDescriptor fd, Rect padding, Options opts) {
    return nativeDecodeFileDescriptor(fd, padding, opts, nativeInBitmap(opts), 0);
  }

  @Implementation(minSdk = Q)
  protected static Bitmap nativeDecodeAsset(
      long nativeAsset, Rect padding, Options opts, long inBitmapHandle, long colorSpaceHandle) {
    return BitmapFactoryNatives.nativeDecodeAsset(
        nativeAsset, padding, opts, inBitmapHandle, colorSpaceHandle);
  }

  @Implementation(minSdk = LOLLIPOP, maxSdk = P)
  protected static Bitmap nativeDecodeAsset(long nativeAsset, Rect padding, Options opts) {
    return nativeDecodeAsset(nativeAsset, padding, opts, nativeInBitmap(opts), 0);
  }

  @Implementation(minSdk = Q)
  protected static Bitmap nativeDecodeByteArray(
      byte[] data,
      int offset,
      int length,
      Options opts,
      long inBitmapHandle,
      long colorSpaceHandle) {
    return BitmapFactoryNatives.nativeDecodeByteArray(
        data, offset, length, opts, inBitmapHandle, colorSpaceHandle);
  }

  @Implementation(maxSdk = P)
  protected static Bitmap nativeDecodeByteArray(byte[] data, int offset, int length, Options opts) {
    return nativeDecodeByteArray(data, offset, length, opts, nativeInBitmap(opts), 0);
  }

  @Implementation
  protected static boolean nativeIsSeekable(FileDescriptor fd) {
    return BitmapFactoryNatives.nativeIsSeekable(fd);
  }

  /** Helper for passing inBitmap's native pointer to native. */
  static long nativeInBitmap(Options opts) {
    if (opts == null || opts.inBitmap == null) {
      return 0;
    }

    return opts.inBitmap.getNativeInstance();
  }

  @ForType(BitmapFactory.class)
  interface BitmapFactoryReflector {
    Bitmap decodeStreamInternal(InputStream is, Rect outPadding, Options opts);

    void setDensityFromOptions(Bitmap outputBitmap, Options opts);

    @Direct
    Bitmap decodeResource(Resources res, int id, BitmapFactory.Options options);
  }

  @ForType(BitmapFactory.Options.class)
  interface BitmapFactoryOptionsReflector {
    void validate(Options opts);
  }

  /** Shadow picker for {@link BitmapFactory}. */
  public static final class Picker extends GraphicsShadowPicker<Object> {
    public Picker() {
      super(ShadowBitmapFactory.class, ShadowNativeBitmapFactory.class);
    }
  }
}