blob: 75ac56bfbbd5a731351fbe88cfeac07e3de0848f [file] [log] [blame]
reedd5b75632015-08-13 09:37:45 -07001/*
2 * Copyright 2015 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "gm/gm.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -04009#include "include/core/SkBitmap.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#include "include/core/SkCanvas.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040011#include "include/core/SkColor.h"
12#include "include/core/SkColorSpace.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050013#include "include/core/SkImage.h"
14#include "include/core/SkImageGenerator.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040015#include "include/core/SkImageInfo.h"
16#include "include/core/SkMatrix.h"
17#include "include/core/SkPaint.h"
18#include "include/core/SkPicture.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050019#include "include/core/SkPictureRecorder.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040020#include "include/core/SkPoint.h"
21#include "include/core/SkRect.h"
22#include "include/core/SkRefCnt.h"
23#include "include/core/SkScalar.h"
24#include "include/core/SkSize.h"
25#include "include/core/SkString.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050026#include "include/core/SkSurface.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040027#include "include/core/SkTypes.h"
Adlai Holler29405382020-07-20 16:02:05 -040028#include "include/gpu/GrDirectContext.h"
Robert Phillips057c33f2020-07-17 11:59:01 -040029#include "include/gpu/GrRecordingContext.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040030#include "include/gpu/GrTypes.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040031#include "include/private/GrTypesPriv.h"
Robert Phillips057c33f2020-07-17 11:59:01 -040032#include "src/gpu/GrRecordingContextPriv.h"
Brian Salomon201cdbb2019-08-14 17:00:30 -040033#include "src/gpu/GrSamplerState.h"
Greg Danielf91aeb22019-06-18 09:58:02 -040034#include "src/gpu/GrTextureProxy.h"
Robert Phillips53eaa642021-08-10 13:49:51 -040035#include "src/gpu/SurfaceContext.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040036#include "src/image/SkImage_Base.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050037#include "src/image/SkImage_Gpu.h"
reedd5b75632015-08-13 09:37:45 -070038
Ben Wagner7fde8e12019-05-01 17:28:53 -040039#include <memory>
40#include <utility>
41
42class GrRecordingContext;
43
reedd5b75632015-08-13 09:37:45 -070044static void draw_something(SkCanvas* canvas, const SkRect& bounds) {
45 SkPaint paint;
46 paint.setAntiAlias(true);
47 paint.setColor(SK_ColorRED);
48 paint.setStyle(SkPaint::kStroke_Style);
49 paint.setStrokeWidth(10);
50 canvas->drawRect(bounds, paint);
51 paint.setStyle(SkPaint::kFill_Style);
52 paint.setColor(SK_ColorBLUE);
53 canvas->drawOval(bounds, paint);
54}
55
56/*
57 * Exercise drawing pictures inside an image, showing that the image version is pixelated
58 * (correctly) when it is inside an image.
59 */
60class ImagePictGM : public skiagm::GM {
reedca2622b2016-03-18 07:25:55 -070061 sk_sp<SkPicture> fPicture;
reed9ce9d672016-03-17 10:51:11 -070062 sk_sp<SkImage> fImage0;
63 sk_sp<SkImage> fImage1;
reedd5b75632015-08-13 09:37:45 -070064public:
65 ImagePictGM() {}
66
67protected:
68 SkString onShortName() override {
69 return SkString("image-picture");
70 }
71
72 SkISize onISize() override {
73 return SkISize::Make(850, 450);
74 }
75
76 void onOnceBeforeDraw() override {
77 const SkRect bounds = SkRect::MakeXYWH(100, 100, 100, 100);
78 SkPictureRecorder recorder;
79 draw_something(recorder.beginRecording(bounds), bounds);
reedca2622b2016-03-18 07:25:55 -070080 fPicture = recorder.finishRecordingAsPicture();
reedd5b75632015-08-13 09:37:45 -070081
82 // extract enough just for the oval.
83 const SkISize size = SkISize::Make(100, 100);
Matt Sarett77a7a1b2017-02-07 13:56:11 -050084 auto srgbColorSpace = SkColorSpace::MakeSRGB();
reedd5b75632015-08-13 09:37:45 -070085
86 SkMatrix matrix;
87 matrix.setTranslate(-100, -100);
Matt Sarette94255d2017-01-09 12:38:59 -050088 fImage0 = SkImage::MakeFromPicture(fPicture, size, &matrix, nullptr,
89 SkImage::BitDepth::kU8, srgbColorSpace);
reedd5b75632015-08-13 09:37:45 -070090 matrix.postTranslate(-50, -50);
91 matrix.postRotate(45);
92 matrix.postTranslate(50, 50);
Matt Sarette94255d2017-01-09 12:38:59 -050093 fImage1 = SkImage::MakeFromPicture(fPicture, size, &matrix, nullptr,
94 SkImage::BitDepth::kU8, srgbColorSpace);
reedd5b75632015-08-13 09:37:45 -070095 }
96
97 void drawSet(SkCanvas* canvas) const {
Mike Reed1f607332020-05-21 12:11:27 -040098 SkMatrix matrix = SkMatrix::Translate(-100, -100);
reedd5b75632015-08-13 09:37:45 -070099 canvas->drawPicture(fPicture, &matrix, nullptr);
reed9ce9d672016-03-17 10:51:11 -0700100 canvas->drawImage(fImage0.get(), 150, 0);
101 canvas->drawImage(fImage1.get(), 300, 0);
reedd5b75632015-08-13 09:37:45 -0700102 }
103
104 void onDraw(SkCanvas* canvas) override {
105 canvas->translate(20, 20);
106
107 this->drawSet(canvas);
108
109 canvas->save();
110 canvas->translate(0, 130);
111 canvas->scale(0.25f, 0.25f);
112 this->drawSet(canvas);
113 canvas->restore();
114
115 canvas->save();
116 canvas->translate(0, 200);
117 canvas->scale(2, 2);
118 this->drawSet(canvas);
119 canvas->restore();
120 }
121
122private:
John Stiles7571f9e2020-09-02 22:42:33 -0400123 using INHERITED = skiagm::GM;
reedd5b75632015-08-13 09:37:45 -0700124};
125DEF_GM( return new ImagePictGM; )
126
reed8f343722015-08-13 13:32:39 -0700127///////////////////////////////////////////////////////////////////////////////////////////////////
128
Robert Phillipscdfdb392020-07-28 15:05:04 -0400129static std::unique_ptr<SkImageGenerator> make_pic_generator(GrDirectContext*,
Robert Phillips057c33f2020-07-17 11:59:01 -0400130 sk_sp<SkPicture> pic) {
reed935d6cf2015-08-18 11:16:09 -0700131 SkMatrix matrix;
132 matrix.setTranslate(-100, -100);
Mike Reed185130c2017-02-15 15:14:16 -0500133 return SkImageGenerator::MakeFromPicture({ 100, 100 }, std::move(pic), &matrix, nullptr,
Matt Sarette94255d2017-01-09 12:38:59 -0500134 SkImage::BitDepth::kU8,
Matt Sarett77a7a1b2017-02-07 13:56:11 -0500135 SkColorSpace::MakeSRGB());
reed935d6cf2015-08-18 11:16:09 -0700136}
137
138class RasterGenerator : public SkImageGenerator {
139public:
Mike Reed4edb5d22017-04-17 11:02:51 -0400140 RasterGenerator(const SkBitmap& bm) : SkImageGenerator(bm.info()), fBM(bm)
141 {}
142
reed935d6cf2015-08-18 11:16:09 -0700143protected:
144 bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
Matt Sarettebb1b5c2017-05-12 11:41:27 -0400145 const Options&) override {
reedc4a83e22015-09-11 11:47:27 -0700146 SkASSERT(fBM.width() == info.width());
147 SkASSERT(fBM.height() == info.height());
Matt Sarettebb1b5c2017-05-12 11:41:27 -0400148 return fBM.readPixels(info, pixels, rowBytes, 0, 0);
reed935d6cf2015-08-18 11:16:09 -0700149 }
150private:
151 SkBitmap fBM;
152};
Robert Phillipscdfdb392020-07-28 15:05:04 -0400153static std::unique_ptr<SkImageGenerator> make_ras_generator(GrDirectContext*,
Robert Phillips057c33f2020-07-17 11:59:01 -0400154 sk_sp<SkPicture> pic) {
reed935d6cf2015-08-18 11:16:09 -0700155 SkBitmap bm;
156 bm.allocN32Pixels(100, 100);
157 SkCanvas canvas(bm);
158 canvas.clear(0);
159 canvas.translate(-100, -100);
160 canvas.drawPicture(pic);
Mike Kleinf46d5ca2019-12-11 10:45:01 -0500161 return std::make_unique<RasterGenerator>(bm);
reed935d6cf2015-08-18 11:16:09 -0700162}
163
reed935d6cf2015-08-18 11:16:09 -0700164class TextureGenerator : public SkImageGenerator {
165public:
Robert Phillips057c33f2020-07-17 11:59:01 -0400166 TextureGenerator(GrRecordingContext* rContext, const SkImageInfo& info, sk_sp<SkPicture> pic)
167 : SkImageGenerator(info)
168 , fRContext(SkRef(rContext)) {
Robert Phillipse2f7d182016-12-15 09:23:05 -0500169
Robert Phillips057c33f2020-07-17 11:59:01 -0400170 sk_sp<SkSurface> surface(SkSurface::MakeRenderTarget(rContext, SkBudgeted::kYes, info, 0,
Robert Phillipse44ef102017-07-21 15:37:19 -0400171 kTopLeft_GrSurfaceOrigin, nullptr));
scroggoe6f0d6e2016-05-13 07:25:44 -0700172 if (surface) {
173 surface->getCanvas()->clear(0);
174 surface->getCanvas()->translate(-100, -100);
175 surface->getCanvas()->drawPicture(pic);
176 sk_sp<SkImage> image(surface->makeImageSnapshot());
Brian Salomond0924f32021-02-03 10:15:31 -0500177 std::tie(fView, std::ignore) = as_IB(image)->asView(rContext, GrMipmapped::kNo);
scroggoe6f0d6e2016-05-13 07:25:44 -0700178 }
reed935d6cf2015-08-18 11:16:09 -0700179 }
180protected:
Robert Phillips057c33f2020-07-17 11:59:01 -0400181 GrSurfaceProxyView onGenerateTexture(GrRecordingContext* rContext,
Brian Salomonc5243782020-04-02 12:50:34 -0400182 const SkImageInfo& info,
183 const SkIPoint& origin,
Brian Salomon7e67dca2020-07-21 09:27:25 -0400184 GrMipmapped mipMapped,
Brian Salomonc5243782020-04-02 12:50:34 -0400185 GrImageTexGenPolicy policy) override {
Robert Phillips057c33f2020-07-17 11:59:01 -0400186 SkASSERT(rContext);
Robert Phillipscdfdb392020-07-28 15:05:04 -0400187 SkASSERT(rContext->priv().matches(fRContext.get()));
reed935d6cf2015-08-18 11:16:09 -0700188
Greg Danielfebdedf2020-02-05 17:06:27 -0500189 if (!fView) {
Greg Danielcc104db2020-02-03 14:17:08 -0500190 return {};
scroggoe6f0d6e2016-05-13 07:25:44 -0700191 }
192
Brian Salomonc5243782020-04-02 12:50:34 -0400193 if (origin.fX == 0 && origin.fY == 0 && info.dimensions() == fView.proxy()->dimensions() &&
194 policy == GrImageTexGenPolicy::kDraw) {
Greg Danielcc104db2020-02-03 14:17:08 -0500195 return fView;
Robert Phillipse2f7d182016-12-15 09:23:05 -0500196 }
Brian Salomon30275712020-04-03 15:56:36 -0400197 auto budgeted = policy == GrImageTexGenPolicy::kNew_Uncached_Unbudgeted ? SkBudgeted::kNo
198 : SkBudgeted::kYes;
Brian Salomonc5243782020-04-02 12:50:34 -0400199 return GrSurfaceProxyView::Copy(
Robert Phillips057c33f2020-07-17 11:59:01 -0400200 fRContext.get(), fView, mipMapped,
Greg Daniel46cfbc62019-06-07 11:43:30 -0400201 SkIRect::MakeXYWH(origin.x(), origin.y(), info.width(), info.height()),
Brian Salomonc5243782020-04-02 12:50:34 -0400202 SkBackingFit::kExact, budgeted);
reed935d6cf2015-08-18 11:16:09 -0700203 }
Robert Phillips4447b642017-03-03 11:10:18 -0500204
reed935d6cf2015-08-18 11:16:09 -0700205private:
Robert Phillips057c33f2020-07-17 11:59:01 -0400206 sk_sp<GrRecordingContext> fRContext;
207 GrSurfaceProxyView fView;
reed935d6cf2015-08-18 11:16:09 -0700208};
Robert Phillips4447b642017-03-03 11:10:18 -0500209
Robert Phillipscdfdb392020-07-28 15:05:04 -0400210static std::unique_ptr<SkImageGenerator> make_tex_generator(GrDirectContext* dContext,
Robert Phillips057c33f2020-07-17 11:59:01 -0400211 sk_sp<SkPicture> pic) {
Robert Phillipscdfdb392020-07-28 15:05:04 -0400212 if (!dContext) {
213 return nullptr;
214 }
215
reed935d6cf2015-08-18 11:16:09 -0700216 const SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100);
217
Robert Phillipscdfdb392020-07-28 15:05:04 -0400218 return std::make_unique<TextureGenerator>(dContext, info, pic);
reed935d6cf2015-08-18 11:16:09 -0700219}
reed8f343722015-08-13 13:32:39 -0700220
221class ImageCacheratorGM : public skiagm::GM {
Robert Phillipscdfdb392020-07-28 15:05:04 -0400222 typedef std::unique_ptr<SkImageGenerator> (*FactoryFunc)(GrDirectContext*, sk_sp<SkPicture>);
Robert Phillips057c33f2020-07-17 11:59:01 -0400223
224 SkString fName;
225 FactoryFunc fFactory;
226 sk_sp<SkPicture> fPicture;
227 sk_sp<SkImage> fImage;
228 sk_sp<SkImage> fImageSubset;
reed8f343722015-08-13 13:32:39 -0700229
230public:
Robert Phillips057c33f2020-07-17 11:59:01 -0400231 ImageCacheratorGM(const char suffix[], FactoryFunc factory) : fFactory(factory) {
reed935d6cf2015-08-18 11:16:09 -0700232 fName.printf("image-cacherator-from-%s", suffix);
233 }
reed8f343722015-08-13 13:32:39 -0700234
235protected:
236 SkString onShortName() override {
reed935d6cf2015-08-18 11:16:09 -0700237 return fName;
reed8f343722015-08-13 13:32:39 -0700238 }
239
240 SkISize onISize() override {
reeda32cc952015-08-19 06:07:29 -0700241 return SkISize::Make(960, 450);
reed8f343722015-08-13 13:32:39 -0700242 }
243
244 void onOnceBeforeDraw() override {
245 const SkRect bounds = SkRect::MakeXYWH(100, 100, 100, 100);
246 SkPictureRecorder recorder;
247 draw_something(recorder.beginRecording(bounds), bounds);
reedca2622b2016-03-18 07:25:55 -0700248 fPicture = recorder.finishRecordingAsPicture();
reed935d6cf2015-08-18 11:16:09 -0700249 }
reed8f343722015-08-13 13:32:39 -0700250
Robert Phillipscdfdb392020-07-28 15:05:04 -0400251 bool makeCaches(GrDirectContext* dContext) {
Robert Phillipsf8237cb2020-07-29 12:22:00 -0400252 {
253 auto gen = fFactory(dContext, fPicture);
254 if (!gen) {
255 return false;
256 }
257 fImage = SkImage::MakeFromGenerator(std::move(gen));
258 if (!fImage) {
259 return false;
260 }
261 SkASSERT(fImage->dimensions() == SkISize::Make(100, 100));
Robert Phillipscdfdb392020-07-28 15:05:04 -0400262 }
reed935d6cf2015-08-18 11:16:09 -0700263
Robert Phillipsf8237cb2020-07-29 12:22:00 -0400264 {
265 const SkIRect subset = SkIRect::MakeLTRB(50, 50, 100, 100);
266
267 // We re-create the generator here on the off chance that making a subset from
268 // 'fImage' might perturb its state.
269 auto gen = fFactory(dContext, fPicture);
270 if (!gen) {
271 return false;
272 }
273 fImageSubset = SkImage::MakeFromGenerator(std::move(gen))->makeSubset(subset, dContext);
274 if (!fImageSubset) {
275 return false;
276 }
277 SkASSERT(fImageSubset->dimensions() == SkISize::Make(50, 50));
Robert Phillipscdfdb392020-07-28 15:05:04 -0400278 }
Robert Phillipsf8237cb2020-07-29 12:22:00 -0400279
Robert Phillipscdfdb392020-07-28 15:05:04 -0400280 return true;
reed935d6cf2015-08-18 11:16:09 -0700281 }
282
Robert Phillipsf8237cb2020-07-29 12:22:00 -0400283 static void draw_placeholder(SkCanvas* canvas, SkScalar x, SkScalar y, int w, int h) {
284 SkPaint paint;
285 paint.setStyle(SkPaint::kStroke_Style);
286 SkRect r = SkRect::MakeXYWH(x, y, SkIntToScalar(w), SkIntToScalar(h));
287 canvas->drawRect(r, paint);
288 canvas->drawLine(r.left(), r.top(), r.right(), r.bottom(), paint);
289 canvas->drawLine(r.left(), r.bottom(), r.right(), r.top(), paint);
290 }
291
Adlai Hollerbcfc5542020-08-27 12:44:07 -0400292 static void draw_as_bitmap(GrDirectContext* dContext, SkCanvas* canvas, SkImage* image,
293 SkScalar x, SkScalar y) {
reed935d6cf2015-08-18 11:16:09 -0700294 SkBitmap bitmap;
Adlai Hollerbcfc5542020-08-27 12:44:07 -0400295 if (as_IB(image)->getROPixels(dContext, &bitmap)) {
Mike Reed34a0c972021-01-25 17:49:32 -0500296 canvas->drawImage(bitmap.asImage(), x, y);
Robert Phillipsf8237cb2020-07-29 12:22:00 -0400297 } else {
298 draw_placeholder(canvas, x, y, image->width(), image->height());
299 }
reed935d6cf2015-08-18 11:16:09 -0700300 }
301
Brian Osmana28e2b02017-04-24 16:44:03 -0400302 static void draw_as_tex(SkCanvas* canvas, SkImage* image, SkScalar x, SkScalar y) {
Robert Phillipsf8237cb2020-07-29 12:22:00 -0400303 // The gpu-backed images are drawn in this manner bc the generator backed images
304 // aren't considered texture-backed
Brian Salomond0924f32021-02-03 10:15:31 -0500305 auto [view, ct] = as_IB(image)->asView(canvas->recordingContext(), GrMipmapped::kNo);
Greg Danielfebdedf2020-02-05 17:06:27 -0500306 if (!view) {
reeda32cc952015-08-19 06:07:29 -0700307 // show placeholder if we have no texture
Robert Phillipsf8237cb2020-07-29 12:22:00 -0400308 draw_placeholder(canvas, x, y, image->width(), image->height());
reed935d6cf2015-08-18 11:16:09 -0700309 return;
310 }
Brian Salomond0924f32021-02-03 10:15:31 -0500311 SkColorInfo colorInfo(GrColorTypeToSkColorType(ct),
312 image->alphaType(),
313 image->refColorSpace());
Brian Salomon729fc0c2019-09-30 16:33:11 +0000314 // No API to draw a GrTexture directly, so we cheat and create a private image subclass
Adlai Holler302e8fb2020-09-14 11:58:06 -0400315 sk_sp<SkImage> texImage(new SkImage_Gpu(sk_ref_sp(canvas->recordingContext()),
Brian Salomond0924f32021-02-03 10:15:31 -0500316 image->uniqueID(),
317 std::move(view),
318 std::move(colorInfo)));
Brian Osmana28e2b02017-04-24 16:44:03 -0400319 canvas->drawImage(texImage.get(), x, y);
reed8f343722015-08-13 13:32:39 -0700320 }
321
Adlai Hollerbcfc5542020-08-27 12:44:07 -0400322 void drawRow(GrDirectContext* dContext, SkCanvas* canvas, float scale) const {
Robert Phillipsf8237cb2020-07-29 12:22:00 -0400323 canvas->scale(scale, scale);
324
Mike Reed1f607332020-05-21 12:11:27 -0400325 SkMatrix matrix = SkMatrix::Translate(-100, -100);
reed8f343722015-08-13 13:32:39 -0700326 canvas->drawPicture(fPicture, &matrix, nullptr);
327
reed935d6cf2015-08-18 11:16:09 -0700328 // Draw the tex first, so it doesn't hit a lucky cache from the raster version. This
329 // way we also can force the generateTexture call.
330
Robert Phillipsf8237cb2020-07-29 12:22:00 -0400331 draw_as_tex(canvas, fImage.get(), 150, 0);
332 draw_as_tex(canvas, fImageSubset.get(), 150+101, 0);
reed935d6cf2015-08-18 11:16:09 -0700333
Adlai Hollerbcfc5542020-08-27 12:44:07 -0400334 draw_as_bitmap(dContext, canvas, fImage.get(), 310, 0);
335 draw_as_bitmap(dContext, canvas, fImageSubset.get(), 310+101, 0);
reed8f343722015-08-13 13:32:39 -0700336 }
337
Robert Phillipscdfdb392020-07-28 15:05:04 -0400338 DrawResult onDraw(SkCanvas* canvas, SkString* errorMsg) override {
Adlai Hollerbcfc5542020-08-27 12:44:07 -0400339 auto dContext = GrAsDirectContext(canvas->recordingContext());
340 if (!this->makeCaches(dContext)) {
Robert Phillipscdfdb392020-07-28 15:05:04 -0400341 errorMsg->printf("Could not create cached images");
342 return DrawResult::kSkip;
343 }
reed935d6cf2015-08-18 11:16:09 -0700344
reed8f343722015-08-13 13:32:39 -0700345 canvas->save();
Robert Phillipsf8237cb2020-07-29 12:22:00 -0400346 canvas->translate(20, 20);
Adlai Hollerbcfc5542020-08-27 12:44:07 -0400347 this->drawRow(dContext, canvas, 1.0);
reed8f343722015-08-13 13:32:39 -0700348 canvas->restore();
349
350 canvas->save();
Robert Phillipsf8237cb2020-07-29 12:22:00 -0400351 canvas->translate(20, 150);
Adlai Hollerbcfc5542020-08-27 12:44:07 -0400352 this->drawRow(dContext, canvas, 0.25f);
reed8f343722015-08-13 13:32:39 -0700353 canvas->restore();
Robert Phillipsf8237cb2020-07-29 12:22:00 -0400354
355 canvas->save();
356 canvas->translate(20, 220);
Adlai Hollerbcfc5542020-08-27 12:44:07 -0400357 this->drawRow(dContext, canvas, 2.0f);
Robert Phillipsf8237cb2020-07-29 12:22:00 -0400358 canvas->restore();
359
Robert Phillipscdfdb392020-07-28 15:05:04 -0400360 return DrawResult::kOk;
reed8f343722015-08-13 13:32:39 -0700361 }
362
363private:
John Stiles7571f9e2020-09-02 22:42:33 -0400364 using INHERITED = skiagm::GM;
reed8f343722015-08-13 13:32:39 -0700365};
Robert Phillipsf8237cb2020-07-29 12:22:00 -0400366
reed935d6cf2015-08-18 11:16:09 -0700367DEF_GM( return new ImageCacheratorGM("picture", make_pic_generator); )
368DEF_GM( return new ImageCacheratorGM("raster", make_ras_generator); )
Brian Osmanc7ad40f2018-05-31 14:27:17 -0400369DEF_GM( return new ImageCacheratorGM("texture", make_tex_generator); )